| ID | Title | Duration (ms) |
|---|---|---|
| 1 | array after should return an empty string when undefined. | 11 |
| 2 | array after should return all of the items in an array after the given index. | 1 |
| 3 | array after should return all of the items in an array after the specified count. | 1 |
| 4 | array after should return all of the items in an array after the specified count. | 1 |
| 5 | array arrayify should arrayify a value. | 2 |
| 6 | array before should return an empty string when undefined. | 1 |
| 7 | array before should return all of the items in an array before the given index. | 1 |
| 8 | array before should return all of the items in an array before the specified count. | 1 |
| 9 | array each should use the key and value of each property in an object inside a block. | 1 |
| 10 | array eachIndex should render the block using the array and each item's index. | 2 |
| 11 | array eachIndex should render the block when non array is passed returning empty string | 1 |
| 12 | array first array should return the first item in a collection. | 1 |
| 13 | array first array should return an array with the first two items in a collection. | 0 |
| 14 | array first array should return an empty string when undefined. | 1 |
| 15 | array first array should return the first item in an array. | 0 |
| 16 | array first array should return an array with the first two items in an array. | 1 |
| 17 | array first string should return an empty string when empty string is provided. | 1 |
| 18 | array first string should return the whole string when the number is big enough. | 0 |
| 19 | array first string should return the expected substring. | 1 |
| 20 | array filter should render the block if the given string is in the array. | 1 |
| 21 | array filter should render the inverse block if the string is not in the array: | 1 |
| 22 | array filter should render a block for each object that has a "first" property with the value "d". | 2 |
| 23 | array forEach should iterate over an array, exposing objects as context. | 1 |
| 24 | array forEach should expose `index` | 1 |
| 25 | array forEach should expose `total` | 1 |
| 26 | array forEach should expose `isFirst` | 1 |
| 27 | array forEach should expose `isLast` | 1 |
| 28 | array forEach should return empty block | 1 |
| 29 | array inArray should render the first block when a value exists in the array. | 1 |
| 30 | array inArray should render the inverse block when a value does not exist. | 1 |
| 31 | array isArray should return true if the value is an array. | 2 |
| 32 | array last array should return an empty string when undefined. | 1 |
| 33 | array last array should return the last item in an array. | 1 |
| 34 | array last array should return an array with the last two items in an array. | 0 |
| 35 | array last array should return an empty array array if non array is passed | 1 |
| 36 | array last string should return an empty string when empty string is provided. | 1 |
| 37 | array last string should return the whole string when the number is big enough. | 0 |
| 38 | array last string should return the expected substring. | 1 |
| 39 | array lengthEqual should render the first block if length is the given number | 1 |
| 40 | array lengthEqual should render the inverse block if length is not the given number | 1 |
| 41 | array map should return an empty string when undefined. | 0 |
| 42 | array map should map the items in the array and return new values. | 1 |
| 43 | array map should work with a string value: | 1 |
| 44 | array map should return an empty string when the array syntax is invalid: | 0 |
| 45 | array some should render the first block if the callback returns true | 1 |
| 46 | array some should render the inverse block if the array is undefined | 1 |
| 47 | array some should render the inverse block if falsey | 1 |
| 48 | array sort should return an empty string when an invalid value is passed: | 0 |
| 49 | array sort should sort the items in the array | 1 |
| 50 | array sort should return all items in an array sorted in lexicographical order. | 0 |
| 51 | array sort should sort the items in the array in reverse order: | 1 |
| 52 | array sortBy should return an empty string when undefined. | 1 |
| 53 | array sortBy should sort the items in an array. | 0 |
| 54 | array sortBy should return an empty string when the array is invalid: | 1 |
| 55 | array sortBy should take a compare function. | 8 |
| 56 | array sortBy should sort based on object key: | 1 |
| 57 | array withAfter should use all of the items in an array after the specified count. | 0 |
| 58 | array withBefore should use all of the items in an array before the specified count. | 1 |
| 59 | array withFirst should use the first item in an array. | 1 |
| 60 | array withFirst should return an empty string when no array is passed: | 0 |
| 61 | array withFirst should return an empty string when no array is passed: | 1 |
| 62 | array withFirst should use the first two items in an array. | 1 |
| 63 | array withLast should return an empty string when undefined. | 0 |
| 64 | array withLast should use the last item in an array. | 1 |
| 65 | array withLast should use the last two items in an array. | 0 |
| 66 | array withSort should return an empty string when array is undefined | 1 |
| 67 | array withSort should sort the array in lexicographical order | 0 |
| 68 | array withSort should sort the array in reverse order | 0 |
| 69 | array withSort should sort the array by deliveries | 1 |
| 70 | array withSort should sort the array by deliveries in reverse order | 1 |
| 71 | collection isEmpty should render the first block when an array is empty. | 1 |
| 72 | collection isEmpty should render the first block when the value is null. | 0 |
| 73 | collection isEmpty should render the second block when an array is not empty. | 1 |
| 74 | collection isEmpty should render the second block when an object is not empty. | 1 |
| 75 | collection isEmpty should render the first block when an object is empty. | 0 |
| 76 | collection iterate object should iterate over a plain object: | 1 |
| 77 | collection iterate object should expose `@key`: | 1 |
| 78 | collection iterate object should render the inverse block when falsey: | 0 |
| 79 | collection iterate array should iterate over an array: | 1 |
| 80 | collection iterate array should expose `@index`: | 1 |
| 81 | collection length should return the length of the array | 0 |
| 82 | collection length should return an empty string when undefined. | 0 |
| 83 | collection length should return empty strig when the value is null. | 1 |
| 84 | collection length should return the length of a string. | 0 |
| 85 | collection length should parse an array passed as a string | 1 |
| 86 | collection length should return 0 when the array is invalid: | 0 |
| 87 | collection length should return 0 when the value is not an array: | 1 |
| 88 | comparison and should render a block if both values are truthy. | 1 |
| 89 | comparison and should render the inverse block if both values are not truthy. | 0 |
| 90 | comparison gt second arg should render the first block if true. | 1 |
| 91 | comparison gt second arg should render the second block if equal. | 0 |
| 92 | comparison gt second arg should render the second block if false. | 0 |
| 93 | comparison gt compare hash should not render a block if the value is not equal to a given number. | 1 |
| 94 | comparison gt compare hash should render a block if the value is greater than a given number. | 1 |
| 95 | comparison gt compare hash should not render a block if the value is less than a given number. | 0 |
| 96 | comparison gte second argument should render the first block if true. | 1 |
| 97 | comparison gte second argument should render the first block if equal. | 0 |
| 98 | comparison gte second argument should render the second block if false. | 1 |
| 99 | comparison gte hash compare should render a block if the value is greater than a given number. | 0 |
| 100 | comparison gte hash compare should render a block if the value is equal to a given number. | 1 |
| 101 | comparison gte hash compare should not render a block if the value is less than a given number. | 0 |
| 102 | comparison has should render a block if the condition is true. | 1 |
| 103 | comparison has should render the inverse block if false. | 1 |
| 104 | comparison has should render the inverse block if value is undefined. | 1 |
| 105 | comparison has should render the inverse block if context is undefined. | 0 |
| 106 | comparison has should work with arrays | 1 |
| 107 | comparison has should work with two strings | 1 |
| 108 | comparison has should return the inverse when the second string is not found | 0 |
| 109 | comparison has should work with object keys | 1 |
| 110 | comparison eq should render a block if the value is equal to a given number. | 0 |
| 111 | comparison eq should render the inverse block if falsey. | 0 |
| 112 | comparison eq should compare first and second args | 1 |
| 113 | comparison ifEven should render the block if the given value is an even number | 0 |
| 114 | comparison ifEven should render the inverse block if the number is odd | 1 |
| 115 | comparison ifNth should render a custom class on even rows | 2 |
| 116 | comparison ifOdd should render the block if the given value is an even number | 0 |
| 117 | comparison ifOdd should render the inverse block if the number is odd | 1 |
| 118 | comparison is should render a block if the condition is true. | 0 |
| 119 | comparison is should use the `compare` arg on the options hash | 2 |
| 120 | comparison is should render the inverse if the condition is false | 0 |
| 121 | comparison isnt should render a block if the condition is not true. | 1 |
| 122 | comparison isnt should use the `compare` arg on the options hash | 0 |
| 123 | comparison isnt should render the inverse if the condition is false | 0 |
| 124 | comparison lt second arg should render the first block if true. | 0 |
| 125 | comparison lt second arg should render the second block if equal. | 0 |
| 126 | comparison lt second arg should render the second block if false. | 0 |
| 127 | comparison lt compare hash should render a block if the value is less than a given number. | 0 |
| 128 | comparison lt compare hash should not render a block if the value is greater than a given number. | 1 |
| 129 | comparison lte second arg should render the first block if true. | 0 |
| 130 | comparison lte second arg should render the first block if equal. | 1 |
| 131 | comparison lte second arg should render the second block if false. | 0 |
| 132 | comparison lte compare hash should render a block if the value is less than a given number. | 0 |
| 133 | comparison lte compare hash should render a block if the value is equal to a given number. | 0 |
| 134 | comparison lte compare hash should not render a block if the value is greater than a given number. | 0 |
| 135 | comparison neither should render a block if one of the values is truthy. | 1 |
| 136 | comparison neither should render the inverse block if neither are true. | 1 |
| 137 | comparison unlessEq should render a block unless the value is equal to a given number. | 0 |
| 138 | comparison unlessEq should render a block unless the value is equal to a given number. | 1 |
| 139 | comparison unlessGt should render a block unless the value is greater than a given number. | 1 |
| 140 | comparison unlessGt should render a block unless the value is greater than a given number. | 0 |
| 141 | comparison unlessLt should render a block unless the value is less than a given number. | 1 |
| 142 | comparison unlessLt should render a block unless the value is less than a given number. | 1 |
| 143 | comparison unlessGteq should render a block unless the value is greater than or equal to a given number. | 0 |
| 144 | comparison unlessGteq should render a block unless the value is greater than or equal to a given number. | 1 |
| 145 | comparison unlessGteq should not render a block unless the value is greater than or equal to a given number. | 0 |
| 146 | comparison unlessLteq should render a block unless the value is less than or equal to a given number. | 1 |
| 147 | comparison unlessLteq should render a block unless the value is less than or equal to a given number. | 0 |
| 148 | comparison unlessLteq should not render a block unless the value is less than or equal to a given number. | 1 |
| 149 | first string should extract the first char when no "n" is given. | 1 |
| 150 | first string should return the whole string when "n" is bigger that string size | 1 |
| 151 | first string should return the expected string | 1 |
| 152 | first string should return empty string when empty string is provided | 0 |
| 153 | first array should extract the first elem when no "n" is given. | 1 |
| 154 | first array should return the whole array when "n" is bigger that array size | 1 |
| 155 | first array should return the expected elems | 0 |
| 156 | first array should return empty string when empty array is provided | 0 |
| 157 | html ellipsis should return an empty string if undefined | 1 |
| 158 | html ellipsis should return then string truncated by a specified length. | 1 |
| 159 | html ellipsis should return the string if shorter than the specified length. | 0 |
| 160 | html sanitize should return an empty string when undefined. | 0 |
| 161 | html sanitize should strip html from a string. | 0 |
| 162 | html ul should should return an unordered list | 1 |
| 163 | html ol should should return an ordered list | 1 |
| 164 | html thumbnailImage {{{thumbnailImage context}}} should return figure with link and caption | 0 |
| 165 | html thumbnailImage {{{thumbnailImage context}}} should return figure with extra class "test" | 1 |
| 166 | html thumbnailImage {{{thumbnailImage context}}} should return figure with image that has class "test" | 0 |
| 167 | html thumbnailImage {{{thumbnailImage context}}} should return figure with link that has class "test" | 1 |
| 168 | html thumbnailImage {{{thumbnailImage context}}} should return figure without link | 0 |
| 169 | html thumbnailImage {{{thumbnailImage context}}} should return figure without caption | 1 |
| 170 | inflection inflect should return the plural or singular form of a word based on a value. | 0 |
| 171 | inflection inflect should return the plural or singular form of a word based on a value and include the count. | 1 |
| 172 | inflection ordinalize should return an ordinalized string. | 1 |
| 173 | last string should extract the last char when no "n" is given. | 1 |
| 174 | last string should return the whole string when "n" is bigger that string size | 1 |
| 175 | last string should return the expected string | 1 |
| 176 | last string should return empty string when empty string is provided | 0 |
| 177 | last array should extract the last elem when no "n" is given. | 1 |
| 178 | last array should return the whole array when "n" is bigger that array size | 1 |
| 179 | last array should return the expected elems | 0 |
| 180 | last array should return empty string when empty array is provided | 1 |
| 181 | markdown markdown should render markdown using the {{#markdown}} block helper | 4 |
| 182 | math add should return the sum of two numbers. | 0 |
| 183 | math average should return the average of a list of numbers: | 1 |
| 184 | math average should return the average of an array of numbers: | 0 |
| 185 | math ceil should return the value rounded up to the nearest integer. | 1 |
| 186 | math divide should return the division of two numbers. | 0 |
| 187 | math floor should return the value rounded down to the nearest integer. | 1 |
| 188 | math multiply should return the multiplication of two numbers. | 0 |
| 189 | math round should return the value rounded to the nearest integer. | 1 |
| 190 | math subtract should return the difference of two numbers. | 0 |
| 191 | math sum should return the sum of multiple numbers. | 1 |
| 192 | math sum should return the sum of multiple numbers. | 0 |
| 193 | math sum should return the total sum of array. | 1 |
| 194 | math sum should return the total sum of array and numbers. | 0 |
| 195 | misc default should use the given value: | 1 |
| 196 | misc default should fallback to the default value when no value exists | 0 |
| 197 | misc noop should be a noop | 1 |
| 198 | misc withHash should return an empty sting | 1 |
| 199 | misc withHash should not blow up when no hash is defined. | 0 |
| 200 | misc withHash should return the inverse hash when defined and the value is falsy. | 1 |
| 201 | misc withHash should return string from the newly created context | 0 |
| 202 | misc withHash should return string from the parent context | 1 |
| 203 | misc withHash should add two attributes to the new context | 1 |
| 204 | number phoneNumber Format a phone number. | 0 |
| 205 | number toFixed should return the value rounded to the nearest integer. | 0 |
| 206 | number toFixed should return the value rounded exactly n digits after the decimal place. | 1 |
| 207 | number toPrecision Returns the number in fixed-point or exponential notation rounded to n significant digits. | 1 |
| 208 | number toPrecision should return the value rounded exactly n digits after the decimal place. | 0 |
| 209 | number toExponential should return the number in fixed-point or exponential notation rounded to n significant digits. | 1 |
| 210 | number toExponential should return the number in fixed-point or exponential notation rounded to exactly n significant digits. | 0 |
| 211 | number toInt should return an integer. | 1 |
| 212 | number toFloat should return a floating point number. | 0 |
| 213 | number addCommas should add commas to a number. | 0 |
| 214 | number toAbbr should abbreviate the given number. | 0 |
| 215 | number toAbbr should abbreviate a number with to the given decimal. | 0 |
| 216 | number toAbbr should round up to the next increment | 0 |
| 217 | number toAbbr should abbreviate a number based on a number and include decimal. | 1 |
| 218 | number random should return a random number between two values. | 1 |
| 219 | object extend should extend multiple objects into one: | 1 |
| 220 | object extend should work as a non-helper util: | 0 |
| 221 | object extend should skip over sparse objects | 0 |
| 222 | object forIn should iterate over each property in an object: | 1 |
| 223 | object forIn should return the inverse block if no object is passed: | 1 |
| 224 | object forIn should expose private variables: | 1 |
| 225 | object forOwn should iterate over each property in an object: | 0 |
| 226 | object forOwn should return the inverse block if no object is passed: | 1 |
| 227 | object forOwn should only expose "own" keys: | 0 |
| 228 | object forOwn should expose private variables: | 1 |
| 229 | object toPath should return a path from provided arguments | 1 |
| 230 | object toPath should return a path from calculated arguments | 0 |
| 231 | object toPath should return a `get` compatible path | 1 |
| 232 | object hasOwn should return true if object has own property: | 1 |
| 233 | object hasOwn should return false if object does not have own property: | 0 |
| 234 | object isObject should return true if value is an object: | 1 |
| 235 | object isObject should return false if value is not an object: | 0 |
| 236 | object merge should deeply merge objects passed on the context: | 1 |
| 237 | object parseJSON should parse a JSON string: | 0 |
| 238 | object stringify should stringify an object: | 1 |
| 239 | string camelcase should return an empty string if undefined | 0 |
| 240 | string camelcase should return the string in camelcase | 1 |
| 241 | string camelcase should lowercase a single character | 1 |
| 242 | string capitalize should return an empty string if undefined | 0 |
| 243 | string capitalize should capitalize a word. | 0 |
| 244 | string capitalizeAll should return an empty string if undefined | 1 |
| 245 | string capitalizeAll should return the string with the every word capitalized. | 0 |
| 246 | string center should return an empty string if undefined | 1 |
| 247 | string center should return the string centered by using non-breaking spaces. | 0 |
| 248 | string chop should return an empty string if undefined | 1 |
| 249 | string chop should remove non-word characters from start of string | 0 |
| 250 | string chop should remove non-word characters from end of string | 0 |
| 251 | string dashcase should return an empty string if undefined | 1 |
| 252 | string dashcase should return the string in dashcase | 1 |
| 253 | string dashcase should lowercase a single character | 0 |
| 254 | string dotcase should return an empty string if undefined | 1 |
| 255 | string dotcase should return the string in dotcase | 1 |
| 256 | string dotcase should lowercase a single character | 0 |
| 257 | string hyphenate should return an empty string if undefined | 1 |
| 258 | string hyphenate should return the string with spaces replaced with hyphens. | 0 |
| 259 | string lowercase should return an empty string if undefined | 0 |
| 260 | string lowercase should return the string in lowercase | 0 |
| 261 | string pascalcase should return an empty string if undefined | 1 |
| 262 | string pascalcase should return the string in pascalcase | 0 |
| 263 | string pascalcase should uppercase a single character | 1 |
| 264 | string pathcase should return an empty string if undefined | 0 |
| 265 | string pathcase should return the string in pathcase | 0 |
| 266 | string pathcase should lowercase a single character | 0 |
| 267 | string plusify should return an empty string if undefined | 0 |
| 268 | string plusify should return the empty string with no change. | 1 |
| 269 | string plusify should return the string with no change. | 0 |
| 270 | string plusify should return the string with spaces replaced with pluses. | 1 |
| 271 | string replace should return an empty string if undefined | 0 |
| 272 | string replace should replace occurrences of string "A" with string "B" | 0 |
| 273 | string replace should return the string if `a` is undefined | 0 |
| 274 | string replace should replace the string with `""` if `b` is undefined | 1 |
| 275 | string reverse should return an empty string if undefined | 0 |
| 276 | string reverse should return the string in reverse. | 0 |
| 277 | string sentence should return an empty string if undefined | 1 |
| 278 | string sentence should capitalize the first word of each sentence in a string and convert the rest of the sentence to lowercase. | 0 |
| 279 | string snakecase should return an empty string if undefined | 1 |
| 280 | string snakecase should lowercase a single character | 0 |
| 281 | string snakecase should return the string in snakecase | 1 |
| 282 | string split should return an empty string if undefined | 0 |
| 283 | string split should split the string with the default character | 1 |
| 284 | string split should split the string on the given character | 0 |
| 285 | string titleize should return an empty string if undefined | 0 |
| 286 | string titleize should return unchanged input string if string has only non-word characters | 1 |
| 287 | string titleize should return the string in title case. | 0 |
| 288 | string trim should return an empty string if undefined | 1 |
| 289 | string trim should trim leading whitespace | 0 |
| 290 | string trim should trim trailing whitespace | 0 |
| 291 | string startsWith should return an empty string if undefined | 1 |
| 292 | string startsWith should render "Yes he is", from inside the block. | 0 |
| 293 | string startsWith should render the Inverse block. | 1 |
| 294 | string startsWith should render the Inverse block. | 0 |
| 295 | string uppercase should return an empty string if undefined | 1 |
| 296 | string uppercase should return the string in uppercase | 0 |
| 297 | string uppercase should work as a block helper | 1 |
| 298 | url urlResolve should take a base URL, and a href URL, and resolve them as a browser would | 1 |
| 299 | url urlResolve should take a base URL, and a href URL, and resolve them as a browser would | 1 |
| 300 | url urlResolve should take a base URL, and a href URL, and resolve them as a browser would | 1 |
| 301 | url encodeURI should return an encoded uri string. | 0 |
| 302 | url decodeURI should return an decoded uri string. | 1 |
| 303 | url urlParse should take a string, and return an object stringified to JSON. | 0 |
| 304 | url strip protocol should take an http url and return without the protocol | 0 |
| 305 | url strip protocol strip https protocol | 0 |
| 306 | url strip protocol should leave a relative url unchanged | 0 |
| 307 | url strip protocol should leave an absolute url unchanged | 0 |
| 308 | filter: should filter strings from the array: | 1 |
| 309 | filter: should filter objects: | 0 |
| 310 | filter: should filter strings: | 0 |
| 311 | filter: should throw an error when the callback is missing. | 1 |
| 312 | flatten should flatten nested arrays: | 0 |
| 313 | flatten should flatten deeply nested arrays: | 0 |
| 314 | errors should throw an error when invalid args are passed: | 1 |
| 315 | empty array should return an empty array when null or undefined is passed | 0 |
| 316 | basic sort should sort an array of primitives | 0 |
| 317 | arraySort should sort by a property: | 1 |
| 318 | arraySort should sort by a nested property: | 0 |
| 319 | arraySort should do nothing when the specified property is not a string: | 1 |
| 320 | arraySort should sort by multiple properties: | 0 |
| 321 | arraySort should support sorting with a list of function: | 0 |
| 322 | arraySort should support sorting with an array of function: | 1 |
| 323 | arraySort should support sorting with any combination of functions and properties: | 1 |
| 324 | arraySort should support reverse sorting with any combination of functions and properties: | 1 |
| 325 | createFrame should create a reference to _parent: | 0 |
| 326 | createFrame should expose a non-enumerable `extend` method: | 1 |
| 327 | createFrame should extend the frame object with the `extend` method: | 0 |
| 328 | createFrame should extend the frame object with additional objects: | 0 |
| 329 | createFrame should work with sparse arguments: | 1 |
| 330 | createFrame should add private variables when passed to `options.fn()`: | 6 |
| 331 | createFrame should create private variables from options hash properties | 3 |
| 332 | createFrame should throw an error if args are invalid: | 0 |
| 333 | .forIn() should loop through all properties in the object. | 1 |
| 334 | .forIn() should break the loop early if `false` is returned. | 0 |
| 335 | forOwn should expose keys and values from the given object | 0 |
| 336 | indexOf should get the index of the given element: | 0 |
| 337 | indexOf should return -1 if fromIndex is out of range: | 0 |
| 338 | indexOf should return -1 if the element does not exist: | 0 |
| 339 | indexOf should return -1 if the array is undefined | 0 |
| 340 | indexOf should get the index, starting from the given index: | 0 |
| 341 | isEven should return true if the number is odd: | 0 |
| 342 | isEven should work with strings: | 0 |
| 343 | isEven should throw an error on bad args: | 0 |
| 344 | isEven should throw an error on non-integer args: | 0 |
| 345 | isExtendable should return true when a value is an object: | 0 |
| 346 | isExtendable should return false when a value is not an object: | 1 |
| 347 | is a number 255 should be a number | 0 |
| 348 | is a number 5000 should be a number | 0 |
| 349 | is a number 0 should be a number | 0 |
| 350 | is a number 0.1 should be a number | 1 |
| 351 | is a number -0.1 should be a number | 0 |
| 352 | is a number -1.1 should be a number | 0 |
| 353 | is a number 37 should be a number | 0 |
| 354 | is a number 3.14 should be a number | 1 |
| 355 | is a number 1 should be a number | 0 |
| 356 | is a number 1.1 should be a number | 0 |
| 357 | is a number 10 should be a number | 0 |
| 358 | is a number 10.1 should be a number | 0 |
| 359 | is a number 100 should be a number | 1 |
| 360 | is a number -100 should be a number | 0 |
| 361 | is a number "0.1" should be a number | 0 |
| 362 | is a number "-0.1" should be a number | 0 |
| 363 | is a number "-1.1" should be a number | 1 |
| 364 | is a number "0" should be a number | 0 |
| 365 | is a number "012" should be a number | 0 |
| 366 | is a number "0xff" should be a number | 0 |
| 367 | is a number "1" should be a number | 0 |
| 368 | is a number "1.1" should be a number | 0 |
| 369 | is a number "10" should be a number | 0 |
| 370 | is a number "10.10" should be a number | 0 |
| 371 | is a number "100" should be a number | 0 |
| 372 | is a number "5e3" should be a number | 0 |
| 373 | is a number " 56\r\n " should be a number | 1 |
| 374 | is a number 0.6931471805599453 should be a number | 0 |
| 375 | is a number 12 should be a number | 0 |
| 376 | is a number 12 should be a number | 0 |
| 377 | is a number 1 should be a number | 0 |
| 378 | is a number 0 should be a number | 0 |
| 379 | is a number 1.5707963267948966 should be a number | 0 |
| 380 | is a number 0.7853981633974483 should be a number | 0 |
| 381 | is a number 0.4636476090008061 should be a number | 0 |
| 382 | is a number 1 should be a number | 0 |
| 383 | is a number 0.5403023058681398 should be a number | 0 |
| 384 | is a number 2.718281828459045 should be a number | 0 |
| 385 | is a number 2.718281828459045 should be a number | 0 |
| 386 | is a number 1 should be a number | 0 |
| 387 | is a number 2.302585092994046 should be a number | 0 |
| 388 | is a number 0.6931471805599453 should be a number | 1 |
| 389 | is a number 0 should be a number | 0 |
| 390 | is a number 0.4342944819032518 should be a number | 0 |
| 391 | is a number 1.4426950408889634 should be a number | 0 |
| 392 | is a number 2 should be a number | 0 |
| 393 | is a number 1 should be a number | 0 |
| 394 | is a number 3.141592653589793 should be a number | 0 |
| 395 | is a number 1 should be a number | 0 |
| 396 | is a number 3125 should be a number | 1 |
| 397 | is a number 0.5197859611550224 should be a number | 0 |
| 398 | is a number 1 should be a number | 0 |
| 399 | is a number 0.8414709848078965 should be a number | 0 |
| 400 | is a number 1 should be a number | 0 |
| 401 | is a number 0.7071067811865476 should be a number | 0 |
| 402 | is a number 1.4142135623730951 should be a number | 0 |
| 403 | is a number 1.5574077246549023 should be a number | 0 |
| 404 | is a number 1.7976931348623157e+308 should be a number | 0 |
| 405 | is a number 5e-324 should be a number | 0 |
| 406 | is a number "0.0" should be a number | 0 |
| 407 | is a number "0x0" should be a number | 0 |
| 408 | is a number "0e+5" should be a number | 0 |
| 409 | is a number "000" should be a number | 0 |
| 410 | is a number "0.0e-5" should be a number | 0 |
| 411 | is a number "0.0E5" should be a number | 0 |
| 412 | is a number 0 should be a number | 0 |
| 413 | is a number 1 should be a number | 1 |
| 414 | is a number 3.14 should be a number | 0 |
| 415 | is a number 37 should be a number | 0 |
| 416 | is a number 5 should be a number | 0 |
| 417 | is a number 0 should be a number | 0 |
| 418 | is a number 0 should be a number | 0 |
| 419 | is a number 0.6931471805599453 should be a number | 0 |
| 420 | is a number 1 should be a number | 0 |
| 421 | is a number 0 should be a number | 1 |
| 422 | is a number 1756258093848 should be a number | 0 |
| 423 | is not a number " " should not be a number | 0 |
| 424 | is not a number "\r\n\t" should not be a number | 0 |
| 425 | is not a number "" should not be a number | 0 |
| 426 | is not a number "" should not be a number | 1 |
| 427 | is not a number "3a" should not be a number | 0 |
| 428 | is not a number "abc" should not be a number | 0 |
| 429 | is not a number "false" should not be a number | 0 |
| 430 | is not a number "null" should not be a number | 0 |
| 431 | is not a number "true" should not be a number | 0 |
| 432 | is not a number "undefined" should not be a number | 0 |
| 433 | is not a number null should not be a number | 0 |
| 434 | is not a number null should not be a number | 0 |
| 435 | is not a number null should not be a number | 0 |
| 436 | is not a number null should not be a number | 0 |
| 437 | is not a number null should not be a number | 0 |
| 438 | is not a number null should not be a number | 0 |
| 439 | is not a number null should not be a number | 0 |
| 440 | is not a number null should not be a number | 0 |
| 441 | is not a number null should not be a number | 1 |
| 442 | is not a number {} should not be a number | 0 |
| 443 | is not a number [1,2,3] should not be a number | 0 |
| 444 | is not a number [1] should not be a number | 0 |
| 445 | is not a number [] should not be a number | 0 |
| 446 | is not a number true should not be a number | 0 |
| 447 | is not a number false should not be a number | 0 |
| 448 | is not a number null should not be a number | 0 |
| 449 | is not a number undefined should not be a number | 1 |
| 450 | is not a number null should not be a number | 0 |
| 451 | is not a number null should not be a number | 0 |
| 452 | is not a number undefined should not be a number | 0 |
| 453 | is not a number null should not be a number | 0 |
| 454 | is not a number "2025-08-27T01:28:13.849Z" should not be a number | 0 |
| 455 | is not a number null should not be a number | 0 |
| 456 | is not a number undefined should not be a number | 1 |
| 457 | is not a number {} should not be a number | 0 |
| 458 | isOdd should return true if the number is odd: | 0 |
| 459 | isOdd should work with strings: | 1 |
| 460 | isOdd should throw an error when an invalid value is passed | 0 |
| 461 | Same-Realm Server Tests should return `true` if the object is created by the `Object` constructor. | 0 |
| 462 | Same-Realm Server Tests should return `false` if the object is not created by the `Object` constructor. | 1 |
| 463 | kindOf null and undefined should work for undefined | 0 |
| 464 | kindOf null and undefined should work for null | 0 |
| 465 | kindOf primitives should work for booleans | 0 |
| 466 | kindOf primitives should work for numbers | 0 |
| 467 | kindOf primitives should work for strings | 0 |
| 468 | kindOf objects should work for arguments | 0 |
| 469 | kindOf objects should work for buffers | 1 |
| 470 | kindOf objects should work for objects | 0 |
| 471 | kindOf objects should work for dates | 0 |
| 472 | kindOf objects should work for arrays | 0 |
| 473 | kindOf objects should work for regular expressions | 0 |
| 474 | kindOf objects should work for functions | 0 |
| 475 | kindOf es6 features should work for Map | 0 |
| 476 | kindOf es6 features should work for WeakMap | 1 |
| 477 | kindOf es6 features should work for Set | 0 |
| 478 | kindOf es6 features should work for WeakSet | 0 |
| 479 | kindOf es6 features should work for Symbol | 0 |
| 480 | make iterator should return source argument if it is already a function with no context | 0 |
| 481 | make iterator should return a function that calls object/deepMatches if argument is an object | 0 |
| 482 | make iterator should return a function that calls object/deepMatches if argument is a regex | 0 |
| 483 | make iterator should return a function that returns the property value if argument is a string | 0 |
| 484 | make iterator should return a function that returns the property value if argument is a number | 0 |
| 485 | make iterator should return an identify function if no args | 0 |
| 486 | make iterator should return an identify function if first arg is `null` | 1 |
| 487 | make iterator should return a function that is called with the specified context | 0 |
| 488 | sync markdown helper should render markdown: | 0 |
| 489 | sync markdown helper should highlight code blocks | 11 |
| 490 | sync markdown helper should pass options to remarkable | 24 |
| 491 | sync markdown helper should pass options to highlight.js: | 1 |
| 492 | sync handlebars: should work as a handlebars helper: | 1 |
| 493 | sync handlebars: should pass hash options to remarkable: | 2 |
| 494 | sync handlebars: should pass hash options to highlight.js: | 2 |
| 495 | .mixinDeep() should deeply mix the properties of object into the first object. | 0 |
| 496 | .mixinDeep() should merge object properties without affecting any object | 0 |
| 497 | .mixinDeep() should do a deep merge | 1 |
| 498 | .mixinDeep() should use the last value defined | 0 |
| 499 | .mixinDeep() should use the last value defined on nested object | 0 |
| 500 | .mixinDeep() should shallow clone when an empty object is passed | 0 |
| 501 | .mixinDeep() should merge additional objects into the first: | 0 |
| 502 | .mixinDeep() should clone objects during merge | 0 |
| 503 | .mixinDeep() should deep clone arrays during merge | 1 |
| 504 | .mixinDeep() should copy source properties | 0 |
| 505 | .mixinDeep() should not clone arrays | 0 |
| 506 | .mixinDeep() should work with sparse objects: | 0 |
| 507 | .mixinDeep() should clone RegExps | 1 |
| 508 | .mixinDeep() should clone Dates | 0 |
| 509 | .mixinDeep() should not clone objects created with custom constructor | 0 |
| 510 | striptags should not modify plain text | 0 |
| 511 | striptags should remove simple HTML tags | 1 |
| 512 | striptags should leave HTML tags if specified | 0 |
| 513 | striptags should leave attributes when allowing HTML | 0 |
| 514 | striptags should leave nested HTML tags if specified | 0 |
| 515 | striptags should leave outer HTML tags if specified | 0 |
| 516 | striptags should remove DOCTYPE declaration | 1 |
| 517 | striptags should remove comments | 0 |
| 518 | striptags should strip <> within quotes | 0 |
| 519 | striptags should strip extra < within tags | 0 |
| 520 | striptags should strip tags within comments | 1 |
| 521 | striptags should strip comment-like tags | 0 |
| 522 | striptags should leave normal exclamation points alone | 0 |
| 523 | striptags should allow an array parameter for allowable tags | 0 |
| 524 | striptags should strip tags when an empty array is provided | 1 |
| 525 | striptags should not fail with nested quotes | 0 |
| 526 | all helper (with single argument) should behave like if helper | 8 |
| 527 | all helper should render "big" if all conditions truthy | 3 |
| 528 | all helper should render empty if any condition is falsy | 5 |
| 529 | any helper (with option hash) should return "big" with matching predicate | 2 |
| 530 | any helper (with option hash) should return nothing without matching predicate | 4 |
| 531 | any helper (with multiple arguments) should return "big" if at least one arg valid | 3 |
| 532 | any helper (with multiple arguments) should return "" when no arguments are valid | 3 |
| 533 | assignVar and getVar helpers should throw an exception if the assignVar key is not a string | 1 |
| 534 | assignVar and getVar helpers should throw an exception if the getVar key is not a string | 1 |
| 535 | assignVar and getVar helpers should assign and get variables | 4 |
| 536 | assignVar and getVar helpers should accept null and undefined as input before any variables are stored | 1 |
| 537 | assignVar and getVar helpers should return empty string if variable is not defined | 0 |
| 538 | assignVar and getVar helpers should return empty string if variable is not *yet* defined | 1 |
| 539 | assignVar and getVar helpers should return integers with correct type | 1 |
| 540 | assignVar and getVar helpers should accept a "SafeString" object as a valid alternative to a string (results should be a string when stored) | 1 |
| 541 | assignVar and getVar helpers should accept a string up to the maximum length of the buffer (Max length: 1024) | 0 |
| 542 | assignVar and getVar helpers should throw an error if buffer is filled (Max length: 1024) | 0 |
| 543 | assignVar and getVar helpers should throw an error if buffer is overflowed (Max length: 1024) | 1 |
| 544 | assignVar and getVar helpers should allow for the writing of up to and including maximum variables (Maximum: 50) | 1 |
| 545 | assignVar and getVar helpers should fail when creating more than the maximum variables (Overflow at: 51) | 2 |
| 546 | assignVar and getVar helpers should allow for more variable assignments when some are deleted | 2 |
| 547 | assignVar and getVar helpers should return undefined accessing proto/constructor | 0 |
| 548 | partial and block helpers should insert partial into the corresponding block | 2 |
| 549 | partial and block helpers should not trigger an error if partial name is empty, fallback case used instead | 2 |
| 550 | partial and block helpers should not trigger an error if block name is empty | 1 |
| 551 | partial and block helpers should successfully render template | 1 |
| 552 | partial and block helpers should successfully render template with context | 1 |
| 553 | partial and block helpers should return empty string if using a reserved object property name | 2 |
| 554 | cdn helper should render the css cdn url and produce resource hints when attribute resourceHint is part of the template | 4 |
| 555 | cdn helper should render the css cdn url and produce resource hint without as/type attribute | 1 |
| 556 | cdn helper should render the css cdn url | 2 |
| 557 | cdn helper should render normal assets cdn url | 4 |
| 558 | cdn helper should not use the cdn url | 1 |
| 559 | cdn helper should return the same value if it is a full url | 3 |
| 560 | cdn helper should delete any . and / from the begining of the path if this is not a full path | 8 |
| 561 | cdn helper should return an empty string if no path is provided | 1 |
| 562 | cdn helper should return a webDav asset if webdav protocol specified | 2 |
| 563 | cdn helper should not return a webDav asset if webdav protocol is not correct | 2 |
| 564 | cdn helper should return a custom CDN asset if protocol is configured | 3 |
| 565 | cdn helper should return a custom CDN asset when using nested helper | 4 |
| 566 | cdn helper should return a custom CDN asset when using nested concat helper | 2 |
| 567 | cdn helper should return a local CDN asset if no cdn url is configured | 4 |
| 568 | cdn helper should not return a custom CDN asset if protocol is not configured | 1 |
| 569 | cdn helper should return basic asset URL if protocol is not correct | 2 |
| 570 | cdn helper should avoid double slash in path | 1 |
| 571 | compare helper should render "big" if all compares match | 5 |
| 572 | compare helper should render empty for all cases | 4 |
| 573 | concat helper should concatanate two strings | 1 |
| 574 | decrementVar helper should throw an exception if the decrementVar key is not a string | 0 |
| 575 | decrementVar helper should correctly decrement | 1 |
| 576 | decrementVar helper should correctly decrement an existing variable | 5 |
| 577 | decrementVar helper should correctly overwrite an existing non-integer variable | 0 |
| 578 | decrementVar helper should correctly return data accession object proto/constructor | 1 |
| 579 | dynamicComponent helper should render all dynamic templates accordingly | 1 |
| 580 | dynamicComponent helper should return undefined when accessing proto/constructor | 2 |
| 581 | earlyHint should create a resource hint with only required properties | 1 |
| 582 | earlyHint should create a resource hint with all the properties | 1 |
| 583 | encodeHtmlEntities helper should return a string with HTML entities encoded | 2 |
| 584 | encodeHtmlEntities helper should return a string with HTML entities encoded with named references | 1 |
| 585 | encodeHtmlEntities helper should return a string with HTML entities encoded with decimal option | 0 |
| 586 | encodeHtmlEntities helper should return a string with HTML entities encoded with named references and decimal option | 1 |
| 587 | encodeHtmlEntities helper should return a string with HTML entities encoded with encodeEverything option | 1 |
| 588 | encodeHtmlEntities helper should return a string with HTML entities encoded with encodeEverything and useNamedReferences option | 0 |
| 589 | encodeHtmlEntities helper should return a string with HTML entities encoded with allowUnsafeSymbols option | 1 |
| 590 | encodeHtmlEntities helper should throw an exception if an invalid named argument is passed | 1 |
| 591 | encodeHtmlEntities helper should throw an exception if a non-string argument is passed | 1 |
| 592 | equals helper should render yes if the value is equal to 5 | 1 |
| 593 | equals helper should render empty string | 1 |
| 594 | for helper should iterate once. | 2 |
| 595 | for helper should iterate 10 times | 2 |
| 596 | for helper should not iterate more than 100 times | 2 |
| 597 | for helper should render w/o context | 2 |
| 598 | for helper should convert strings to integers and iterate 10 times | 1 |
| 599 | for helper should not iterate if "from" is less than "to" | 0 |
| 600 | get helper gets a nested prop | 0 |
| 601 | get helper does not access prototype properties | 1 |
| 602 | get helper accepts SafeString paths | 1 |
| 603 | get helper gets context from options object if none was given | 1 |
| 604 | get helper renders to the empty string if no args are passed | 0 |
| 605 | get helper returns the object arg if it is a string, number, undefined, or null | 1 |
| 606 | get helper returns undefined if prop path does not exist | 1 |
| 607 | getContentImage helper should throw an exception if a url is passed | 1 |
| 608 | getContentImage helper should throw an exception if a non-string is passed | 4 |
| 609 | getContentImage helper should return an original image if no size is passed | 1 |
| 610 | getContentImage helper should return an original image if invalid sizes are passed | 1 |
| 611 | getContentImage helper should return a sized image | 2 |
| 612 | getContentImage helper should support lossy compression parameter | 2 |
| 613 | getContentImageSrcset helper should throw an exception if a url is passed | 1 |
| 614 | getContentImageSrcset helper should throw an exception if a non-string is passed | 1 |
| 615 | getContentImageSrcset helper should return a valid srcset | 1 |
| 616 | getContentImageSrcset helper should support lossy compression parameter | 1 |
| 617 | getFontLoaderConfig should return the expected font config | 1 |
| 618 | getFontsCollection should return a font link with fonts from theme settings and &display=swap when no font-display value is passed | 1 |
| 619 | getFontsCollection should return a font link with fonts from theme settings and &display=swap when font-display value passed is invalid | 0 |
| 620 | getFontsCollection should return a font link with fonts from theme settings and &display=${font-display} when valid font-display value is passed | 0 |
| 621 | getFontsCollection should not crash if a malformed Google font is passed when no font-display value is passed | 1 |
| 622 | getFontsCollection should not crash if a malformed Google font is passed when valid font-display value is passed | 1 |
| 623 | getFontsCollection should not crash if a malformed Google font is passed when invalid font-display value is passed | 1 |
| 624 | getImage helper should return a url if a url is passed | 0 |
| 625 | getImage helper should return empty if image is invalid | 1 |
| 626 | getImage helper should use the preset from _images | 2 |
| 627 | getImage helper should use the size from the theme_settings | 0 |
| 628 | getImage helper should use the default image url if image is invalid | 1 |
| 629 | getImage helper should use original size if not default is passed | 1 |
| 630 | getImage helper should default to max value (width & height) if value is not provided | 1 |
| 631 | getImage helper should default to size of the image dimensions if known and a larger size is requested | 1 |
| 632 | getImage helper should support lossy compression parameter | 2 |
| 633 | getImageManagerImage helper should throw an exception if a url is passed | 1 |
| 634 | getImageManagerImage helper should throw an exception if a non-string is passed | 1 |
| 635 | getImageManagerImage helper should return an original image if no size is passed | 0 |
| 636 | getImageManagerImage helper should return an original image if invalid sizes are passed | 1 |
| 637 | getImageManagerImage helper should return a sized image | 2 |
| 638 | getImageManagerImage helper should support lossy compression parameter | 2 |
| 639 | getImageManagerImageSrcset helper should throw an exception if a url is passed | 0 |
| 640 | getImageManagerImageSrcset helper should throw an exception if a non-string is passed | 1 |
| 641 | getImageManagerImageSrcset helper should return a valid srcset | 1 |
| 642 | getImageManagerImageSrcset helper should support lossy compression parameter | 1 |
| 643 | getImageSrcset helper should return a srcset if a valid image and srcset sizes are passed | 2 |
| 644 | getImageSrcset helper should return a srcset made of default sizes if requested | 1 |
| 645 | getImageSrcset helper should return a srcset made of default sizes up to the width of the image if known | 1 |
| 646 | getImageSrcset helper should return empty string if no parameters are passed | 1 |
| 647 | getImageSrcset helper should return a srcset without a descriptor if a valid image and single srcset size is passed | 1 |
| 648 | getImageSrcset helper should return a url if a url is passed | 1 |
| 649 | getImageSrcset helper should return empty if srcset array is invalid | 1 |
| 650 | getImageSrcset helper should return empty if image is invalid | 1 |
| 651 | getImageSrcset helper should use the default image url if image is invalid | 1 |
| 652 | getImageSrcset helper should support lossy compression parameter | 2 |
| 653 | getImageSrcset1x2x helper should return a srcset if a valid image and srcset sizes are passed | 2 |
| 654 | getImageSrcset1x2x helper should return an image url alone (1x) if the 2x size is not within constraints | 1 |
| 655 | getImageSrcset1x2x helper should return an image url alone (1x) if image does not have dimesions | 4 |
| 656 | getImageSrcset1x2x helper should throw an exception if an no size argument is passed | 1 |
| 657 | getImageSrcset1x2x helper should throw an exception if an invalid size argument is passed | 0 |
| 658 | getImageSrcset1x2x helper should support lossy compression parameter | 2 |
| 659 | getObject helper returns requested prop in correct object type | 1 |
| 660 | getObject helper does not access prototype props | 1 |
| 661 | getObject helper accepts SafeString paths | 1 |
| 662 | getShortMonth helper should render an object properly | 3 |
| 663 | helperMissing should return empty string if the helper is missing | 0 |
| 664 | if helper should have the same behavior as the original if helper | 4 |
| 665 | if helper should render "big" if all conditions match | 5 |
| 666 | if helper should render empty for all cases | 4 |
| 667 | if helper should ignore additional arguments and process only the first three arguments | 1 |
| 668 | if helper should throw an exception when non string value sent to gtnum | 1 |
| 669 | if helper should throw an exception when NaN value sent to gtnum | 2 |
| 670 | if helper should render "big" if all ifs match | 4 |
| 671 | if helper should render empty for all cases | 8 |
| 672 | if helper should work as a non-block helper when used as a subexpression | 2 |
| 673 | unless helper should print hello | 1 |
| 674 | unless helper should print empty | 1 |
| 675 | unless helper should work with arrays | 1 |
| 676 | unless helper should work as a non-block helper when used as a subexpression | 1 |
| 677 | incrementVar helper should throw an exception if the incrementVar key is not a string | 1 |
| 678 | incrementVar helper should correctly increment | 1 |
| 679 | incrementVar helper should correctly increment an existing variable | 2 |
| 680 | incrementVar helper should correctly overwrite an existing non-integer variable | 1 |
| 681 | incrementVar helper should correctly return data accession object proto/constructor | 1 |
| 682 | inject helper should inject variables | 1 |
| 683 | join helper should print a list of names | 1 |
| 684 | join helper should print a list of names and limit to 3 | 1 |
| 685 | join helper should print a list of names and use "and" for the last name | 0 |
| 686 | join helper should print a list of names and limit to 3 and use "and" for the last name | 1 |
| 687 | json helper should render object to json format | 0 |
| 688 | json helper should work together with getImage | 0 |
| 689 | json helper should work together with concat | 1 |
| 690 | JSONParseSafe helper #Block: should execute the main instruction | 1 |
| 691 | JSONParseSafe helper #Block: should skip the main instruction if variable is non-json | 1 |
| 692 | JSONParseSafe helper #Block: should execute the else instruction if variable is non-json | 1 |
| 693 | JSONParseSafe helper Inline: Output should match the same as `main test` | 0 |
| 694 | JSONParseSafe helper Inline: Using JSON.stringify helper, and a empty object; parse the empty object and output it | 1 |
| 695 | JSONParseSafe helper Inline: Using JSON.stringify helper, and a invalid / malformed object; parse the invalid object and output it, output is a Javascript `undefined` object | 1 |
| 696 | JSONParseSafe helper Inline: Stringifed example - with values | 0 |
| 697 | JSONParseSafe helper Inline: Stringifed example - without values | 1 |
| 698 | JSONParseSafe helper Inline: Stringifed example - malformed input | 0 |
| 699 | lang helper should translate the key with attributes | 0 |
| 700 | lang helper should return an empty string if translator is undefined | 1 |
| 701 | lang helper should return an empty string if translastor returns an object | 1 |
| 702 | langJson helper should return translation as JSON string if translator is defined | 0 |
| 703 | langJson helper should return an empty object as JSON string if translator is not defined | 0 |
| 704 | common utils getValue should get a value from an object | 0 |
| 705 | common utils getValue should get nested values | 1 |
| 706 | common utils getValue should get nested values from arrays | 0 |
| 707 | common utils getValue should get nested values from objects in arrays | 0 |
| 708 | common utils getValue should return obj[String(path)] or undefined if path is not a string or array | 0 |
| 709 | common utils getValue should return the whole object if path is empty | 1 |
| 710 | common utils getValue should return obj if path is falsey | 0 |
| 711 | common utils getValue should return undefined if prop does not exist | 0 |
| 712 | common utils getValue should treat backslash-escaped . characters as part of a prop name | 1 |
| 713 | common utils getValue should not access inherited props | 0 |
| 714 | common utils appendLossyParam should append compression=lossy to URLs without query params | 0 |
| 715 | common utils appendLossyParam should append compression=lossy to URLs with existing query params | 0 |
| 716 | common utils appendLossyParam should not modify URL when lossy is false | 0 |
| 717 | common utils appendLossyParam should not modify URL when lossy is undefined | 1 |
| 718 | common utils appendLossyParam should not modify URL when lossy is not a boolean | 0 |
| 719 | resource hints addResourceHint creates resource hints when valid params are provided | 0 |
| 720 | resource hints addResourceHint creates resource hints when valid params are provided defaulting cors to no when no provided | 1 |
| 721 | resource hints addResourceHint does not add a hint when provided path is invalid | 0 |
| 722 | resource hints addResourceHint does create a hint when no type is provided | 0 |
| 723 | resource hints addResourceHint does not create a hint when provided state param is not supported | 1 |
| 724 | resource hints addResourceHint does not create duplicate, by src, hints, and returns the found src | 0 |
| 725 | resource hints addResourceHint does not create any hint when the limit of allowed hints was reached | 0 |
| 726 | resource hints addResourceHint should throw when invalid cors value is provided | 0 |
| 727 | resource hints addResourceHint should handle URLs with special chars | 0 |
| 728 | resource hints addResourceHint should throw when invalid URL is passed in | 1 |
| 729 | limit helper should limit an array properly | 1 |
| 730 | limit helper should limit an string properly | 0 |
| 731 | moment helper renders the date in the format specified | 196 |
| 732 | moment helper is backwards compatible with helper-date 0.2.3 null-argument behavior | 3 |
| 733 | moment helper permits use of moment.js functions | 1 |
| 734 | moment helper when amount is provided runs console.warn | 1 |
| 735 | money helper should correctly set money value from money site settings | 1 |
| 736 | money helper should read money site settings from input parameters | 2 |
| 737 | money helper should correctly set currency position regardless of location capitalization - [STRF-10878] | 1 |
| 738 | money helper should throw an exception if the price value parameter has an invalid type | 0 |
| 739 | money helper should throw an exception if the decimal places parameter has an invalid type | 1 |
| 740 | multiConcat helper should concatenate all strings by default | 1 |
| 741 | multiConcat helper should accept string, number, boolean, empty | 2 |
| 742 | nl2br helper should convert new lines to <br> tags | 0 |
| 743 | nonce helper should render a nonce in quotes with the correct value from request params | 1 |
| 744 | nonce helper should not render a nonce since request param is empty | 5 |
| 745 | occurrences helper should count number of occurrences of substring | 2 |
| 746 | occurrences helper should ignore non-strings | 2 |
| 747 | occurrences helper should ignore empty strings | 1 |
| 748 | option helper returns the nested prop of this.options | 1 |
| 749 | option helper does not access prototype props | 0 |
| 750 | option helper accepts SafeString paths | 1 |
| 751 | or helper should return "big" if at least one arg valid | 2 |
| 752 | or helper should return "" when no arguments are valid | 1 |
| 753 | pluck helper should get the values from all elements in collection | 1 |
| 754 | pluck helper should return undefined when accessing proto/constructor | 0 |
| 755 | pluck helper should return empty array when null is provided | 1 |
| 756 | pre helper should render an object properly | 1 |
| 757 | pre helper should scape html entities | 1 |
| 758 | Region Helper should return an empty container if using empty content context | 0 |
| 759 | Region Helper should return an empty container if no matching region on context object | 1 |
| 760 | Region Helper should return without region translation data attribute if no translation is provided | 1 |
| 761 | Region Helper should return Hello World | 0 |
| 762 | replace helper should replace all ocurrance of %%var%% with "day" | 1 |
| 763 | replace helper should handle undefined values | 1 |
| 764 | replace helper should replace $ | 2 |
| 765 | replace helper should work nicely with other helpers that use safestring | 0 |
| 766 | replace helper should gracefully handle not strings | 1 |
| 767 | resourceHints should return the expected resource links | 2 |
| 768 | setURLQueryParam helper should return a URL with an added parameter given correct input | 1 |
| 769 | setURLQueryParam helper should return a URL with an updated parameter given correct input | 1 |
| 770 | setURLQueryParam helper should throw an exception if a url is passed with no parameters | 1 |
| 771 | setURLQueryParam helper should throw an exception if a url is passed with only one parameter | 1 |
| 772 | setURLQueryParam helper should throw an exception if an invalid URL is passed | 2 |
| 773 | snippet helper should render a comment | 1 |
| 774 | strReplace helper should replace all by default | 1 |
| 775 | strReplace helper should remove characters from string | 0 |
| 776 | strReplace helper should replace multiple if given quantity | 2 |
| 777 | strReplace helper should throw an exception if the parameters have an invalid type | 2 |
| 778 | stripQuerystring helper strips the query string from a given url | 0 |
| 779 | stripQuerystring helper should work with the getImageSrcset helper as a nested expression | 1 |
| 780 | stripQuerystring helper should work with the getImage helper as a nested expression | 1 |
| 781 | stylesheet helper should render a link tag with the cdn ulr and stencil-stylesheet data tag | 2 |
| 782 | stylesheet helper should render both link tags correclty but produce only one resource hint | 1 |
| 783 | stylesheet helper should render a link tag and all extra attributes with no cdn url | 0 |
| 784 | stylesheet helper should render a link with empty href and no resource hint | 1 |
| 785 | stylesheet helper should add configId to the filename | 1 |
| 786 | stylesheet helper should not append configId if the file is not in assets/css/ directory | 4 |
| 787 | third party handlebars-helpers array helpers after helper returns all the items in an array after the index | 0 |
| 788 | third party handlebars-helpers array helpers first helper returns the first n items in an array | 0 |
| 789 | third party handlebars-helpers collection helpers length helper returns the length of the array | 1 |
| 790 | third party handlebars-helpers comparison helpers contains helper renders the contains block if it evaluates to true | 0 |
| 791 | third party handlebars-helpers comparison helpers contains helper renders the else block if it evaluates to false | 1 |
| 792 | third party handlebars-helpers comparison helpers contains helper renders the contains block if it evaluates to true. object as input | 1 |
| 793 | third party handlebars-helpers comparison helpers contains helper renders the else block if it evaluates to false. object as input | 0 |
| 794 | third party handlebars-helpers comparison helpers contains helper renders the else block if it evaluates to false. null / number as input | 1 |
| 795 | third party handlebars-helpers html helpers contains ellipsis truncates a string to the specified length and appends an ellipsis | 1 |
| 796 | third party handlebars-helpers html helpers contains thumbnailImage creates a <figure> with a thumbnail linked to an image | 0 |
| 797 | third party handlebars-helpers inflection helpers contains ordinalize returns an ordinalized number as a string | 1 |
| 798 | third party handlebars-helpers markdown helpers contains markdown converts a string of markdown to HTML | 1 |
| 799 | third party handlebars-helpers math helpers contains avg returns the average of the numbers in an array | 0 |
| 800 | third party handlebars-helpers object helpers contains isObject returns true if the value is an object | 1 |
| 801 | third party handlebars-helpers object helpers contains isObject returns false if the value is not an object | 0 |
| 802 | third party handlebars-helpers string helpers contains capitalize capitalizes the first word in a sentence | 1 |
| 803 | toLowerCase helper should convert string to lower case | 1 |
| 804 | toLowerCase helper should properly handle values other than strings | 1 |
| 805 | truncate helper should return the entire string if length is longer than the input string | 1 |
| 806 | truncate helper should return the first length number of characters | 0 |
| 807 | truncate helper should return the first argument, coerced to a string, if it is not a string | 1 |
| 808 | truncate helper should handle non-English strings | 1 |
| 809 | truncate helper should handle empty strings | 1 |
| 810 | truncate helper should handle null object | 0 |
| 811 | truncate helper should handle unicode strings | 1 |
| 812 | typeof inline should return string | 0 |
| 813 | typeof inline should return number | 1 |
| 814 | typeof inline should return boolean | 0 |
| 815 | typeof inline should return object: null | 1 |
| 816 | typeof inline should return object: undefined | 0 |
| 817 | typeof inline should return object: object | 1 |
| 818 | typeof inline should return object: array | 0 |
| 819 | typeof inline should return bigInt | 1 |
| 820 | typeof inline should return function | 0 |
| 821 | typeof inline should work with output from another function | 1 |
| 822 | typeof inline should not accept more than one value, throw a error | 0 |
| 823 | typeof inline should not work as a block helper, throw a error | 1 |
| 824 | helper registration loads all the helpers | 1 |
| 825 | switching handlebars versions defaults to v3 | 0 |
| 826 | switching handlebars versions can load v3 | 0 |
| 827 | switching handlebars versions can load v4 | 1 |
| 828 | helper registration loads the helpers and registers them with handlebars | 0 |
| 829 | helper context puts empty site settings into the helper context when not provided | 0 |
| 830 | helper context puts site settings into the helper context when provided | 0 |
| 831 | helper context puts site settings into the helper context when provided after construction | 0 |
| 832 | helper context puts empty theme settings into the helper context when not provided | 0 |
| 833 | helper context puts theme settings into the helper context when provided | 0 |
| 834 | helper context puts theme settings into the helper context when provided after construction | 1 |
| 835 | helper context puts translator accessor into the helper context | 0 |
| 836 | helper context puts page content accessor into the helper context | 0 |
| 837 | helper context puts handlebars environment into the helper context | 1 |
| 838 | helper context puts a handle to global storage into the helper context | 0 |
| 839 | addTemplates registers preprocessed templates with handlebars | 1 |
| 840 | addTemplates registers raw templates with handlebars | 0 |
| 841 | addTemplates registers a mix of preprocessed and raw templates with handlebars | 0 |
| 842 | addTemplates throws FormatError if templates were precompiled with the wrong compiler version | 1 |
| 843 | addTemplates preProcessor throws CompileError when given bad templates | 1 |
| 844 | addTemplates can render using registered partials | 1 |
| 845 | addTemplates isTemplateLoaded tells you if a template has been loaded | 0 |
| 846 | addTemplates does not override templates if called twice | 0 |
| 847 | render can render using registered partials | 0 |
| 848 | render renders without a context | 0 |
| 849 | render sets the locale in the context if a translator is supplied | 0 |
| 850 | render sets the template path in the context | 0 |
| 851 | render applies decorators | 0 |
| 852 | render can use helpers | 0 |
| 853 | render throws TemplateNotFound if missing template | 0 |
| 854 | render throws RenderError if given bad template | 0 |
| 855 | render throws DecoratorError if decorator fails | 0 |
| 856 | renderString renders the given template with the given context | 0 |
| 857 | renderString can use helpers | 1 |
| 858 | renderString renders without a context | 1 |
| 859 | renderString throws CompileError if given a non-string template | 0 |
| 860 | renderString throws PrecompileError if given malformed template | 0 |
| 861 | errors has an accessor to get custom error classes | 1 |
| 862 | logging log helper uses given logger | 1 |
| 863 | logging log helper should not be called, when log level = error | 0 |
| 864 | logging log helper should not be called, when log level = warning | 0 |
| 865 | logging console log helper uses given logger | 1 |
| 866 | logging console log helper should not be called, when log level = error | 1 |
| 867 | logging console log helper should not be called, when log level = warning | 1 |
| 868 | logging should override default console.log | 1 |
| 869 | logging should override default console.info | 0 |
| 870 | logging should override default console.error | 0 |
| 871 | logging should override default console.warn | 1 |
| 872 | logging should check that property access denied message is set as info | 1 |
| 873 | logging should check that property access denied message is set as error | 1 |
| 874 | object manipulation fix shouldnt print when use renderString | 1 |
| 875 | object manipulation fix shouldnt print when use render | 1 |
| 876 | AppError is an instance of Error | 0 |
| 877 | AppError keeps track of the message | 1 |
| 878 | AppError keeps track of the extra details | 0 |
| 879 | AppError supplies default details hash | 0 |
| 880 | AppError makes the error name available | 0 |
| 881 | AppError does not include AppError constructor in stack trace | 1 |
| 882 | subclassing is an instance of Error | 0 |
| 883 | subclassing makes the error name available | 0 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const helpersList = [ | |
| 2 | 'all', | ||
| 3 | 'any', | ||
| 4 | 'assignVar', | ||
| 5 | 'block', | ||
| 6 | 'cdn', | ||
| 7 | 'compare', | ||
| 8 | 'concat', | ||
| 9 | 'contains', | ||
| 10 | 'decrementVar', | ||
| 11 | 'dynamicComponent', | ||
| 12 | 'encodeHtmlEntities', | ||
| 13 | 'for', | ||
| 14 | 'get', | ||
| 15 | 'getContentImage', | ||
| 16 | 'getContentImageSrcset', | ||
| 17 | 'getFontLoaderConfig', | ||
| 18 | 'getFontsCollection', | ||
| 19 | 'getImage', | ||
| 20 | 'getImageManagerImage', | ||
| 21 | 'getImageManagerImageSrcset', | ||
| 22 | 'getImageSrcset', | ||
| 23 | 'getImageSrcset1x2x', | ||
| 24 | 'getObject', | ||
| 25 | 'getVar', | ||
| 26 | 'helperMissing', | ||
| 27 | 'if', | ||
| 28 | 'incrementVar', | ||
| 29 | 'inject', | ||
| 30 | 'join', | ||
| 31 | 'jsContext', | ||
| 32 | 'json', | ||
| 33 | 'jsonParseSafe', | ||
| 34 | 'lang', | ||
| 35 | 'langJson', | ||
| 36 | 'limit', | ||
| 37 | 'moment', | ||
| 38 | 'money', | ||
| 39 | 'multiConcat', | ||
| 40 | 'nl2br', | ||
| 41 | 'occurrences', | ||
| 42 | 'option', | ||
| 43 | 'or', | ||
| 44 | 'partial', | ||
| 45 | 'pluck', | ||
| 46 | 'pre', | ||
| 47 | 'region', | ||
| 48 | 'replace', | ||
| 49 | 'resourceHints', | ||
| 50 | 'setURLQueryParam', | ||
| 51 | 'snippets', | ||
| 52 | 'stripQuerystring', | ||
| 53 | 'strReplace', | ||
| 54 | 'stylesheet', | ||
| 55 | 'thirdParty', | ||
| 56 | 'toLowerCase', | ||
| 57 | 'truncate', | ||
| 58 | 'unless', | ||
| 59 | 'earlyHint', | ||
| 60 | 'nonce', | ||
| 61 | 'typeof' | ||
| 62 | ]; | ||
| 63 | |||
| 64 | 1 | const deprecatedHelpersList = [ | |
| 65 | 'enumerate', | ||
| 66 | 'equals', | ||
| 67 | 'getShortMonth', | ||
| 68 | 'pick' | ||
| 69 | ]; | ||
| 70 | |||
| 71 | 1 | let helpers = []; | |
| 72 | |||
| 73 | 1 | helpersList.forEach(helper => { | |
| 74 | helpers = [...helpers, ...require(`./helpers/${helper}.js`)]; | ||
| 75 | }) | ||
| 76 | |||
| 77 | 1 | deprecatedHelpersList.forEach(helper => { | |
| 78 | helpers = [...helpers, ...require(`./helpers/deprecated/${helper}.js`)]; | ||
| 79 | }) | ||
| 80 | |||
| 81 | // Export full list of helpers | ||
| 82 | 1 | module.exports = helpers; | |
| 83 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const HandlebarsV3 = require('handlebars'); | |
| 3 | 1 | const HandlebarsV4 = require('@bigcommerce/handlebars-v4'); | |
| 4 | 1 | const helpers = require('./helpers'); | |
| 5 | |||
| 6 | 1 | const AppError = require('./lib/appError'); | |
| 7 | 1 | const { CompileError, FormatError, RenderError, DecoratorError, TemplateNotFoundError, ValidationError, PrecompileError } = require('./lib/errors'); | |
| 8 | |||
| 9 | 1 | const handlebarsOptions = { | |
| 10 | preventIndent: true | ||
| 11 | }; | ||
| 12 | |||
| 13 | // HandlebarsRenderer implements the interface Paper requires for its | ||
| 14 | // rendering needs, and does so with Handlebars. | ||
| 15 | class HandlebarsRenderer { | ||
| 16 | // Add static accessor to reference custom errors | ||
| 17 | static get errors() { | ||
| 18 | return { | ||
| 19 | AppError, | ||
| 20 | CompileError, | ||
| 21 | FormatError, | ||
| 22 | RenderError, | ||
| 23 | DecoratorError, | ||
| 24 | TemplateNotFoundError, | ||
| 25 | ValidationError, | ||
| 26 | PrecompileError, | ||
| 27 | }; | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Constructor | ||
| 32 | * | ||
| 33 | * @param {Object} siteSettings - Global site settings, passed to helpers | ||
| 34 | * @param {Object} themeSettings - Theme settings (configuration), passed to helpers | ||
| 35 | * @param {String} hbVersion - Which version of handlebars to use. One of ['v3', 'v4'] - defaults to 'v3'. | ||
| 36 | * @param {Object} logger - A console-like object to use for logging | ||
| 37 | * @param {String} logLevel - log level which will be overriden by renderer | ||
| 38 | */ | ||
| 39 | constructor(siteSettings, themeSettings, hbVersion, logger = console, logLevel = 'info', params = {}) { | ||
| 40 | // Figure out which version of Handlebars to use. | ||
| 41 | switch(hbVersion) { | ||
| 42 | case 'v4': | ||
| 43 | 62 | this.handlebars = HandlebarsV4.create(); | |
| 44 | 62 | break; | |
| 45 | case 'v3': | ||
| 46 | default: | ||
| 47 | 615 | this.handlebars = HandlebarsV3.create(); | |
| 48 | 615 | break; | |
| 49 | } | ||
| 50 | |||
| 51 | 677 | this.logger = logger; | |
| 52 | 677 | this._setHandlebarsLogger(); | |
| 53 | 677 | this._overrideConsoleLog(); | |
| 54 | 677 | this.setSiteSettings(siteSettings || {}); | |
| 55 | 677 | this.setThemeSettings(themeSettings || {}); | |
| 56 | 677 | this.setTranslator(null); | |
| 57 | 677 | this.setContent({}); | |
| 58 | 677 | this.resetDecorators(); | |
| 59 | 677 | this.setLoggerLevel(logLevel); | |
| 60 | 677 | this.setRequestParams(params); | |
| 61 | |||
| 62 | // Build global context for helpers | ||
| 63 | 677 | this.helperContext = { | |
| 64 | handlebars: this.handlebars, | ||
| 65 | getSiteSettings: this.getSiteSettings.bind(this), | ||
| 66 | getThemeSettings: this.getThemeSettings.bind(this), | ||
| 67 | getRequestParams: this.getRequestParams.bind(this), | ||
| 68 | getTranslator: this.getTranslator.bind(this), | ||
| 69 | getContent: this.getContent.bind(this), | ||
| 70 | getLogger: this.getLogger.bind(this), | ||
| 71 | storage: {}, // global storage used by helpers to keep state | ||
| 72 | resourceHints: [] | ||
| 73 | }; | ||
| 74 | |||
| 75 | // Register helpers with Handlebars | ||
| 76 | 677 | for (let i = 0; i < helpers.length; i++) { | |
| 77 | 113736 | const spec = helpers[i]; | |
| 78 | 113736 | this.handlebars.registerHelper(spec.name, spec.factory(this.helperContext)); | |
| 79 | } | ||
| 80 | |||
| 81 | 677 | this.overrideHelpers(); | |
| 82 | } | ||
| 83 | |||
| 84 | getResourceHints() { | ||
| 85 | return this.helperContext.resourceHints; | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * Set the paper.Translator instance used to translate strings in helpers. | ||
| 90 | * | ||
| 91 | * @param {Translator} translator A paper.Translator instance used to translate strings in helpers | ||
| 92 | */ | ||
| 93 | setTranslator(translator) { | ||
| 94 | this._translator = translator; | ||
| 95 | }; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Get the paper.Translator instance used to translate strings in helpers. | ||
| 99 | * | ||
| 100 | * @return {Translator} A paper.Translator instance used to translate strings in helpers | ||
| 101 | */ | ||
| 102 | getTranslator() { | ||
| 103 | return this._translator; | ||
| 104 | }; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Set the siteSettings object containing global site settings. | ||
| 108 | * | ||
| 109 | * @param {object} settings An object containing global site settings. | ||
| 110 | */ | ||
| 111 | setSiteSettings(settings) { | ||
| 112 | this._siteSettings = settings; | ||
| 113 | }; | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Get the siteSettings object containing global site settings. | ||
| 117 | * | ||
| 118 | * @return {object} settings An object containing global site settings. | ||
| 119 | */ | ||
| 120 | getSiteSettings() { | ||
| 121 | return this._siteSettings; | ||
| 122 | }; | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Set the themeSettings object containing the theme configuration. | ||
| 126 | * | ||
| 127 | * @param {object} settings An object containing the theme configuration. | ||
| 128 | */ | ||
| 129 | setThemeSettings(settings) { | ||
| 130 | this._themeSettings = settings; | ||
| 131 | }; | ||
| 132 | |||
| 133 | /** | ||
| 134 | * Get the themeSettings object containing the theme configuration. | ||
| 135 | * | ||
| 136 | * @return {object} settings An object containing the theme configuration. | ||
| 137 | */ | ||
| 138 | getThemeSettings() { | ||
| 139 | return this._themeSettings; | ||
| 140 | }; | ||
| 141 | |||
| 142 | |||
| 143 | /** | ||
| 144 | * Set the request params object containing the request parameters. | ||
| 145 | * | ||
| 146 | * @param {object} params | ||
| 147 | */ | ||
| 148 | setRequestParams(params) { | ||
| 149 | this._params = params; | ||
| 150 | } | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Get the request params object containing the request parameters. | ||
| 154 | * | ||
| 155 | * @returns {object} params | ||
| 156 | */ | ||
| 157 | getRequestParams() { | ||
| 158 | return this._params; | ||
| 159 | } | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Reset decorator list. | ||
| 163 | */ | ||
| 164 | resetDecorators() { | ||
| 165 | this._decorators = []; | ||
| 166 | }; | ||
| 167 | |||
| 168 | /** | ||
| 169 | * Add a decorator to be applied at render time. | ||
| 170 | * | ||
| 171 | * @param {Function} decorator | ||
| 172 | */ | ||
| 173 | addDecorator(decorator) { | ||
| 174 | this._decorators.push(decorator); | ||
| 175 | }; | ||
| 176 | |||
| 177 | /** | ||
| 178 | * Setup content regions to be used by the `region` helper. | ||
| 179 | * | ||
| 180 | * @param {Object} Regions with widgets | ||
| 181 | */ | ||
| 182 | setContent(regions) { | ||
| 183 | this._contentRegions = regions; | ||
| 184 | }; | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Get content regions. | ||
| 188 | * | ||
| 189 | * @param {Object} Regions with widgets | ||
| 190 | */ | ||
| 191 | getContent() { | ||
| 192 | return this._contentRegions; | ||
| 193 | }; | ||
| 194 | |||
| 195 | /** | ||
| 196 | * Get logger provided to the library | ||
| 197 | * | ||
| 198 | * @param {Object} logger | ||
| 199 | */ | ||
| 200 | getLogger() { | ||
| 201 | return this.logger; | ||
| 202 | } | ||
| 203 | |||
| 204 | /** | ||
| 205 | * Add templates to the active set of partials. The templates can either be raw | ||
| 206 | * template strings, or the result coming from the preProcessor function. | ||
| 207 | * | ||
| 208 | * @param {Object} A set of templates to register with handlebars | ||
| 209 | */ | ||
| 210 | addTemplates(templates) { | ||
| 211 | const paths = Object.keys(templates); | ||
| 212 | |||
| 213 | 41 | for (let i = 0; i < paths.length; i++) { | |
| 214 | 83 | const path = paths[i]; | |
| 215 | |||
| 216 | 83 | if (typeof this.handlebars.partials[path] !== 'undefined') { | |
| 217 | 1 | continue; | |
| 218 | } | ||
| 219 | |||
| 220 | 82 | try { | |
| 221 | // Check if it is a precompiled template | ||
| 222 | 82 | const template = this._tryRestoringPrecompiled(templates[path]); | |
| 223 | |||
| 224 | // Register it with handlebars | ||
| 225 | 81 | this.handlebars.registerPartial(path, template); | |
| 226 | } catch(e) { | ||
| 227 | 1 | throw new FormatError(e.message); | |
| 228 | } | ||
| 229 | } | ||
| 230 | }; | ||
| 231 | |||
| 232 | _tryRestoringPrecompiled(precompiled) { | ||
| 233 | // Let's analyze the string to make sure it at least looks | ||
| 234 | // something like a handlebars precompiled template. It should | ||
| 235 | // be a string representation of an object containing a `main` | ||
| 236 | // function and a `compiler` array. We do this because the next | ||
| 237 | // step is a potentially dangerous eval. | ||
| 238 | const re = /"compiler":\[.*\],"main":function/; | ||
| 239 | 82 | if (!re.test(precompiled)) { | |
| 240 | // This is not a valid precompiled template, so this is | ||
| 241 | // a raw template that can be registered directly. | ||
| 242 | 4 | return precompiled; | |
| 243 | } | ||
| 244 | |||
| 245 | // We need to take the string representation and turn it into a | ||
| 246 | // valid JavaScript object. eval is evil, but necessary in this case. | ||
| 247 | 78 | let template; | |
| 248 | 78 | eval(`template = ${precompiled}`); | |
| 249 | |||
| 250 | // Take the precompiled object and get the actual function out of it, | ||
| 251 | // after first testing for runtime version compatibility. | ||
| 252 | 78 | return this.handlebars.template(template); | |
| 253 | } | ||
| 254 | |||
| 255 | /** | ||
| 256 | * Detect whether a given template has been loaded. | ||
| 257 | */ | ||
| 258 | isTemplateLoaded(path) { | ||
| 259 | return typeof this.handlebars.partials[path] !== 'undefined'; | ||
| 260 | } | ||
| 261 | |||
| 262 | /** | ||
| 263 | * Return a function that performs any preprocessing we want to do on the templates. | ||
| 264 | * In our case, run them through the Handlebars precompiler. This returns a string | ||
| 265 | * representation of an object understood by Handlebars to be a precompiled template. | ||
| 266 | */ | ||
| 267 | getPreProcessor() { | ||
| 268 | return templates => { | ||
| 269 | const paths = Object.keys(templates); | ||
| 270 | 40 | const processed = {}; | |
| 271 | |||
| 272 | 40 | for (let i = 0; i < paths.length; i++) { | |
| 273 | 82 | const path = paths[i]; | |
| 274 | 82 | try { | |
| 275 | 82 | processed[path] = this.handlebars.precompile(templates[path], handlebarsOptions); | |
| 276 | } catch(e) { | ||
| 277 | 1 | throw new CompileError(e.message, { path }); | |
| 278 | } | ||
| 279 | } | ||
| 280 | 39 | return processed; | |
| 281 | }; | ||
| 282 | } | ||
| 283 | |||
| 284 | /** | ||
| 285 | * Render a template with the given context | ||
| 286 | * | ||
| 287 | * @param {String} path The path to the template | ||
| 288 | * @param {Object} context The context to provide to the template | ||
| 289 | * @return {Promise} A promise to return the rendered template | ||
| 290 | * @throws [TemplateNotFoundError|RenderError|DecoratorError] | ||
| 291 | */ | ||
| 292 | render(path, context) { | ||
| 293 | return new Promise((resolve, reject) => { | ||
| 294 | context = context || {}; | ||
| 295 | |||
| 296 | // Add some data to the context | ||
| 297 | 18 | context.template = path; | |
| 298 | 18 | if (this._translator) { | |
| 299 | 1 | context.locale_name = this._translator.getLocale(); | |
| 300 | } | ||
| 301 | |||
| 302 | 18 | delete this.handlebars.compile; | |
| 303 | |||
| 304 | // Look up the template | ||
| 305 | 18 | const template = this.handlebars.partials[path]; | |
| 306 | 18 | if (typeof template === 'undefined') { | |
| 307 | 1 | return reject(new TemplateNotFoundError(`template not found: ${path}`)); | |
| 308 | } | ||
| 309 | |||
| 310 | // Render the template | ||
| 311 | 17 | let result; | |
| 312 | 17 | try { | |
| 313 | 17 | result = template(context); | |
| 314 | } catch(e) { | ||
| 315 | 2 | return reject(new RenderError(`${e.message} : ${e.stack}`)); | |
| 316 | } | ||
| 317 | |||
| 318 | // Apply decorators | ||
| 319 | 15 | try { | |
| 320 | 15 | for (let i = 0; i < this._decorators.length; i++) { | |
| 321 | 2 | result = this._decorators[i](result); | |
| 322 | } | ||
| 323 | } catch(e) { | ||
| 324 | 1 | return reject(new DecoratorError(e.message)); | |
| 325 | } | ||
| 326 | |||
| 327 | 14 | resolve(result); | |
| 328 | }); | ||
| 329 | }; | ||
| 330 | |||
| 331 | /** | ||
| 332 | * Renders a string with the given context | ||
| 333 | * | ||
| 334 | * @param {String} template | ||
| 335 | * @param {Object} context | ||
| 336 | * @return {Promise} | ||
| 337 | * @throws [CompileError|RenderError] | ||
| 338 | */ | ||
| 339 | renderString(template, context) { | ||
| 340 | return new Promise((resolve, reject) => { | ||
| 341 | let precompiled, precompiledTemplate; | ||
| 342 | 622 | context = context || {}; | |
| 343 | |||
| 344 | 622 | if (typeof template !== 'string') { | |
| 345 | 1 | return reject(new CompileError('Template must be a string')); | |
| 346 | } | ||
| 347 | |||
| 348 | 621 | try { | |
| 349 | 621 | delete this.handlebars.compile; | |
| 350 | 621 | precompiled = this.handlebars.precompile(template, handlebarsOptions); | |
| 351 | } catch (e) { | ||
| 352 | 6 | return reject(new PrecompileError(e.message)); | |
| 353 | } | ||
| 354 | |||
| 355 | 615 | try { | |
| 356 | 615 | eval(`precompiledTemplate = ${precompiled}`); | |
| 357 | 615 | template = this.handlebars.template(precompiledTemplate); | |
| 358 | } catch(e) { | ||
| 359 | return reject(new CompileError(e.message)); | ||
| 360 | } | ||
| 361 | |||
| 362 | // Render the result | ||
| 363 | 615 | let result; | |
| 364 | 615 | try { | |
| 365 | 615 | result = template(context); | |
| 366 | } catch(e) { | ||
| 367 | 42 | return reject(new RenderError(`${e.message} : ${e.stack}`)); | |
| 368 | } | ||
| 369 | |||
| 370 | 573 | resolve(result); | |
| 371 | }); | ||
| 372 | } | ||
| 373 | |||
| 374 | /** | ||
| 375 | * Internal method. Set the Handlebars logger to use the given console alternative. This is an override | ||
| 376 | * of https://github.com/wycats/handlebars.js/blob/148b19182d70278237a62d8293db540483a0c46c/lib/handlebars/logger.js#L22 | ||
| 377 | */ | ||
| 378 | _setHandlebarsLogger() { | ||
| 379 | // Normalize on the v4 implementation | ||
| 380 | this.handlebars.logger = HandlebarsV4.logger; | ||
| 381 | |||
| 382 | // Override logger.log to use the given console alternative | ||
| 383 | 677 | this.handlebars.log = this.handlebars.logger.log = (level, ...message) => { | |
| 384 | level = this.handlebars.logger.lookupLevel(level); | ||
| 385 | |||
| 386 | 5 | if (this.handlebars.logger.lookupLevel(this.handlebars.logger.level) <= level) { | |
| 387 | 3 | let method = this.handlebars.logger.methodMap[level]; | |
| 388 | 3 | if (typeof this.logger[method] !== 'function') { | |
| 389 | 1 | method = 'log'; | |
| 390 | 2 | } else if (this._overrideHandlebarsAccessDeniedToPropertyMessageLevel(...message)) { | |
| 391 | 1 | method = 'info'; | |
| 392 | } | ||
| 393 | 3 | this.logger[method](...message); | |
| 394 | } | ||
| 395 | }; | ||
| 396 | } | ||
| 397 | |||
| 398 | /** | ||
| 399 | * As some handlebars helpers do not use the logger, we need to override the console.log method | ||
| 400 | */ | ||
| 401 | _overrideConsoleLog() { | ||
| 402 | this.isLoggerOverriden = false; | ||
| 403 | 677 | if (this.logger !== console) { | |
| 404 | 10 | console.log = this.logger.info.bind(this.logger); | |
| 405 | 10 | console.info = this.logger.info.bind(this.logger); | |
| 406 | 10 | console.error = this.logger.error.bind(this.logger); | |
| 407 | 10 | console.warn = this.logger.warn.bind(this.logger); | |
| 408 | 10 | this.isLoggerOverriden = true; | |
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | _overrideHandlebarsAccessDeniedToPropertyMessageLevel(message) { | ||
| 413 | return this.isLoggerOverriden && message.includes('Handlebars: Access has been denied to resolve the property'); | ||
| 414 | } | ||
| 415 | |||
| 416 | /** | ||
| 417 | * | ||
| 418 | * @param {String} level | ||
| 419 | */ | ||
| 420 | setLoggerLevel(level) { | ||
| 421 | this.handlebars.logger.level = level; | ||
| 422 | } | ||
| 423 | |||
| 424 | overrideHelpers() { | ||
| 425 | if (this.isLoggerOverriden) { | ||
| 426 | 10 | this.handlebars.registerHelper('log', () => undefined); | |
| 427 | } | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | 1 | module.exports = HandlebarsRenderer; | |
| 432 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Yield block only if all args are valid | ||
| 7 | * | ||
| 8 | * @example | ||
| 9 | * {{#all items theme_settings.optionA theme_settings.optionB}} ... {{/all}} | ||
| 10 | */ | ||
| 11 | 1 | const factory = () => { | |
| 12 | return function (...args) { | ||
| 13 | |||
| 14 | // Take the last arg which is a Handlebars options object out of args array | ||
| 15 | const opts = args.pop(); | ||
| 16 | |||
| 17 | // Check if all the args are valid / truthy | ||
| 18 | 23 | const result = args.every(function (arg) { | |
| 19 | if (utils.isArray(arg)) { | ||
| 20 | 2 | return !!arg.length; | |
| 21 | } | ||
| 22 | // If an empty object is passed, arg is false | ||
| 23 | 35 | else if (utils.isEmpty(arg) && utils.isObject(arg)) { | |
| 24 | 1 | return false; | |
| 25 | } | ||
| 26 | // Everything else | ||
| 27 | else { | ||
| 28 | 34 | return !!arg; | |
| 29 | } | ||
| 30 | }); | ||
| 31 | |||
| 32 | // If everything was valid, then "all" condition satisfied | ||
| 33 | 23 | if (result) { | |
| 34 | 14 | return opts.fn(this); | |
| 35 | } else { | ||
| 36 | 9 | return opts.inverse(this); | |
| 37 | } | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | |||
| 41 | 1 | module.exports = [{ | |
| 42 | name: 'all', | ||
| 43 | factory: factory, | ||
| 44 | }]; | ||
| 45 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const deepMatches = require('./3p/utils/lib/deepMatches'); | |
| 5 | |||
| 6 | /** | ||
| 7 | * Yield block if any object within a collection matches supplied predicate | ||
| 8 | * | ||
| 9 | * @example | ||
| 10 | * {{#any items selected=true}} ... {{/any}} | ||
| 11 | */ | ||
| 12 | 1 | const factory = () => { | |
| 13 | return function (...args) { | ||
| 14 | |||
| 15 | let any; | ||
| 16 | // Take the last arg which is a Handlebars options object out of args array | ||
| 17 | 19 | const opts = args.pop(); | |
| 18 | 19 | const predicate = opts.hash; | |
| 19 | |||
| 20 | 19 | if (!utils.isEmpty(predicate)) { | |
| 21 | 10 | if (utils.isArray(args[0])) { | |
| 22 | // With options hash, we check the contents of first argument | ||
| 23 | 6 | any = args[0].some((v) => deepMatches(v, predicate)); | |
| 24 | } | ||
| 25 | } else { | ||
| 26 | // DEPRECATED: Moved to #or helper | ||
| 27 | // Without options hash, we check all the arguments | ||
| 28 | 9 | any = args.some(function (arg) { | |
| 29 | if (utils.isArray(arg)) { | ||
| 30 | 3 | return !!arg.length; | |
| 31 | } | ||
| 32 | // If an empty object is passed, arg is false | ||
| 33 | 11 | else if (utils.isEmpty(arg) && utils.isObject(arg)) { | |
| 34 | 1 | return false; | |
| 35 | } | ||
| 36 | // Everything else | ||
| 37 | else { | ||
| 38 | 10 | return !!arg; | |
| 39 | } | ||
| 40 | }); | ||
| 41 | } | ||
| 42 | |||
| 43 | 19 | if (any) { | |
| 44 | 7 | return opts.fn(this); | |
| 45 | } | ||
| 46 | |||
| 47 | 12 | return opts.inverse(this); | |
| 48 | }; | ||
| 49 | }; | ||
| 50 | |||
| 51 | 1 | module.exports = [{ | |
| 52 | name: 'any', | ||
| 53 | factory: factory, | ||
| 54 | }]; | ||
| 55 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const max_length = 1024; | |
| 4 | 1 | const max_keys = 50; | |
| 5 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 6 | 1 | const common = require("./lib/common"); | |
| 7 | |||
| 8 | 1 | const factory = globals => { | |
| 9 | return function (key, value) { | ||
| 10 | |||
| 11 | // Validate that key is a string | ||
| 12 | if (!utils.isString(key)) { | ||
| 13 | 1 | throw new ValidationError("assignVar helper key must be a string"); | |
| 14 | } | ||
| 15 | |||
| 16 | // Setup storage | ||
| 17 | 188 | if (typeof globals.storage.variables === 'undefined') { | |
| 18 | 26 | globals.storage.variables = Object.create(null); | |
| 19 | } | ||
| 20 | |||
| 21 | //Check for if the assigned value is being set to null or undefined | ||
| 22 | 188 | if (!(value === null || value === undefined)) { | |
| 23 | |||
| 24 | // Due to check for string length, we need to unwrap Handlebars.SafeString | ||
| 25 | 179 | value = common.unwrapIfSafeString(globals.handlebars, value); | |
| 26 | |||
| 27 | // Validate that value is a string or Number (int/float) | ||
| 28 | 179 | if (!(utils.isString(value) || value === "") && !Number.isFinite(value)) { | |
| 29 | throw new ValidationError("assignVar helper value must be a string or a number (integer/float)"); | ||
| 30 | } | ||
| 31 | |||
| 32 | // Validate that string is not longer than or equal to the max length | ||
| 33 | 179 | if (utils.isString(value) && value.length >= max_length) { | |
| 34 | 2 | throw new ValidationError(`assignVar helper value must be less than ${max_length} characters, | |
| 35 | but a ${value.length} character value was set to ${key}`); | ||
| 36 | } | ||
| 37 | |||
| 38 | // Make sure the number of total keys is within the limit | ||
| 39 | 177 | if (Object.keys(globals.storage.variables).length >= max_keys) { | |
| 40 | 1 | throw new ValidationError(`Unique keys in variable storage may not exceed ${max_keys} in total`); | |
| 41 | } | ||
| 42 | |||
| 43 | // Store value for later use by getVar helper | ||
| 44 | 176 | globals.storage.variables[key] = value; | |
| 45 | } else { | ||
| 46 | // Delete value from storage as it is now unset. | ||
| 47 | 9 | delete globals.storage.variables[key]; | |
| 48 | } | ||
| 49 | }; | ||
| 50 | }; | ||
| 51 | |||
| 52 | 1 | module.exports = [{ | |
| 53 | name: 'assignVar', | ||
| 54 | factory: factory, | ||
| 55 | // Expose for use in testing to prevent magic numbers that can de-sync. | ||
| 56 | max_length: max_length, | ||
| 57 | max_keys: max_keys | ||
| 58 | }]; | ||
| 59 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const common = require('./lib/common.js'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function(name) { | ||
| 8 | name = common.unwrapIfSafeString(globals.handlebars, name); | ||
| 9 | 4 | if (!utils.isString(name)) { | |
| 10 | 1 | globals.getLogger().info("Non-string passed to block helper"); | |
| 11 | 1 | return ''; | |
| 12 | } | ||
| 13 | |||
| 14 | 3 | if (Object.getOwnPropertyNames(Object.prototype).includes(name)) { | |
| 15 | 1 | globals.getLogger().info(`Invalid name '${name}' passed to the partial helper. Returning empty string.`); | |
| 16 | 1 | return ''; | |
| 17 | } | ||
| 18 | |||
| 19 | 2 | const options = arguments[arguments.length - 1]; | |
| 20 | |||
| 21 | /* Look for partial by name. */ | ||
| 22 | 2 | const partial = globals.handlebars.partials[name] || options.fn; | |
| 23 | 2 | return partial(this, { data: options.hash }); | |
| 24 | }; | ||
| 25 | }; | ||
| 26 | |||
| 27 | 1 | module.exports = [{ | |
| 28 | name: 'block', | ||
| 29 | factory: factory, | ||
| 30 | }]; | ||
| 31 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const cdnify = require('./lib/cdnify'); | |
| 4 | 1 | const {addResourceHint} = require('./lib/resourceHints'); | |
| 5 | 1 | const utils = require("./3p/utils"); | |
| 6 | |||
| 7 | |||
| 8 | 1 | const factory = globals => { | |
| 9 | function addHint(fullPath, options) { | ||
| 10 | 3 | return addResourceHint( | |
| 11 | globals, | ||
| 12 | fullPath, | ||
| 13 | options.hash.resourceHint, | ||
| 14 | options.hash.as, | ||
| 15 | options.hash.crossorigin | ||
| 16 | ); | ||
| 17 | } | ||
| 18 | |||
| 19 | 677 | const cdn = cdnify(globals); | |
| 20 | |||
| 21 | 677 | return function (path) { | |
| 22 | let fullPath = cdn(path); | ||
| 23 | |||
| 24 | 50 | const options = arguments[arguments.length - 1]; | |
| 25 | if ( options.hash && options.hash.resourceHint) { |
||
| 26 | if ( !utils.isString(fullPath) ) { |
||
| 27 | console.info(`Skipping resource hint creation in CDN fill path was not valid [${fullPath}]`); | ||
| 28 | return fullPath; | ||
| 29 | } | ||
| 30 | |||
| 31 | 3 | const hintPath = addHint(fullPath, options); | |
| 32 | if ( utils.isString(hintPath) ) { |
||
| 33 | 3 | return hintPath; | |
| 34 | } | ||
| 35 | |||
| 36 | console.info(`Resource hint path was invalid [${hintPath}]`); | ||
| 37 | } | ||
| 38 | |||
| 39 | 47 | return fullPath; | |
| 40 | }; | ||
| 41 | }; | ||
| 42 | |||
| 43 | 1 | module.exports = [{ | |
| 44 | name: 'cdn', | ||
| 45 | factory: factory, | ||
| 46 | }]; | ||
| 47 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 3 | |||
| 4 | 1 | const factory = () => { | |
| 5 | return function(lvalue, rvalue) { | ||
| 6 | const options = arguments[arguments.length - 1]; | ||
| 7 | 20 | var operator; | |
| 8 | 20 | var operators; | |
| 9 | 20 | var result; | |
| 10 | |||
| 11 | if ( arguments.length < 3 ) { |
||
| 12 | throw new ValidationError("Handlerbars Helper 'compare' needs 2 parameters"); | ||
| 13 | } | ||
| 14 | |||
| 15 | operator = options.hash.operator || "==" ; |
||
| 16 | |||
| 17 | 20 | operators = { | |
| 18 | '==': function (l, r) { return l == r; }, | ||
| 19 | '===': function (l, r) { return l === r; }, | ||
| 20 | '!=': function (l, r) { return l != r; }, | ||
| 21 | '!==': function (l, r) { return l !== r; }, | ||
| 22 | '<': function (l, r) { return l < r; }, | ||
| 23 | '>': function (l, r) { return l > r; }, | ||
| 24 | '<=': function (l, r) { return l <= r; }, | ||
| 25 | '>=': function (l, r) { return l >= r; }, | ||
| 26 | 'typeof': function (l, r) { return typeof l == r; } | ||
| 27 | }; | ||
| 28 | |||
| 29 | // getOwnPropertyNames is used because checking the property | ||
| 30 | // directly (like operators[x]) is insecure | ||
| 31 | // (we could use switch instead) | ||
| 32 | if ( Object.getOwnPropertyNames(operators).indexOf(operator) === -1 ) { |
||
| 33 | throw new ValidationError("Handlerbars Helper 'compare' doesn't know the operator " + operator); | ||
| 34 | } | ||
| 35 | |||
| 36 | 20 | result = operators[operator](lvalue, rvalue); | |
| 37 | |||
| 38 | 20 | if (result) { | |
| 39 | 10 | return options.fn(this); | |
| 40 | } else { | ||
| 41 | 10 | return options.inverse(this); | |
| 42 | } | ||
| 43 | }; | ||
| 44 | }; | ||
| 45 | |||
| 46 | 1 | module.exports = [{ | |
| 47 | name: 'compare', | ||
| 48 | factory: factory, | ||
| 49 | }]; | ||
| 50 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Concats two values, primarily used as a subhelper | ||
| 5 | * @example | ||
| 6 | * {{@lang (concat 'products.reviews.rating.' this) }} | ||
| 7 | */ | ||
| 8 | 1 | const factory = globals => { | |
| 9 | return function(value, otherValue) { | ||
| 10 | return new globals.handlebars.SafeString(value + otherValue); | ||
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | 1 | module.exports = [{ | |
| 15 | name: 'concat', | ||
| 16 | factory: factory, | ||
| 17 | }]; | ||
| 18 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | |||
| 4 | 1 | const utils = require('./3p/utils'); | |
| 5 | |||
| 6 | /** | ||
| 7 | * Is any value included in a collection or a string? | ||
| 8 | * | ||
| 9 | * @example | ||
| 10 | * {{#contains fonts "Roboto"}} ... {{/contains}} | ||
| 11 | * {{#contains font_path "Roboto"}} ... {{/contains}} | ||
| 12 | */ | ||
| 13 | 1 | const factory = () => { | |
| 14 | return function(container, value) { | ||
| 15 | const options = arguments[arguments.length - 1]; | ||
| 16 | 6 | const preparedContainer = utils.isObject(container) ? Object.values(container) : container; | |
| 17 | 6 | const contained = preparedContainer && preparedContainer.includes ? preparedContainer.includes(value) : false; | |
| 18 | |||
| 19 | // Yield block if true | ||
| 20 | 6 | if (contained) { | |
| 21 | 2 | return options.fn(this); | |
| 22 | } else { | ||
| 23 | 4 | return options.inverse(this); | |
| 24 | } | ||
| 25 | }; | ||
| 26 | }; | ||
| 27 | |||
| 28 | 1 | module.exports = [{ | |
| 29 | name: 'contains', | ||
| 30 | factory: factory, | ||
| 31 | }]; | ||
| 32 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 4 | |||
| 5 | 1 | const max_keys = 50; | |
| 6 | |||
| 7 | 1 | const factory = globals => { | |
| 8 | return function (key) { | ||
| 9 | if (!utils.isString(key)) { | ||
| 10 | 1 | throw new ValidationError("decrementVar helper key must be a string"); | |
| 11 | } | ||
| 12 | |||
| 13 | // Setup storage | ||
| 14 | 9 | if (typeof globals.storage.variables === 'undefined') { | |
| 15 | 3 | globals.storage.variables = Object.create(null); | |
| 16 | } | ||
| 17 | |||
| 18 | 9 | if (Object.hasOwnProperty.call(globals.storage.variables, key) && Number.isInteger(globals.storage.variables[key])) { | |
| 19 | // Decrement value if it already exists | ||
| 20 | 5 | globals.storage.variables[key] -= 1; | |
| 21 | } else { | ||
| 22 | // Make sure the number of total keys is within the limit | ||
| 23 | if ( Object.keys(globals.storage.variables).length >= max_keys ) { |
||
| 24 | throw new ValidationError(`Unique keys in variable storage may not exceed ${max_keys} in total`); | ||
| 25 | } | ||
| 26 | // Initialize or re-initialize value | ||
| 27 | 4 | globals.storage.variables[key] = 0; | |
| 28 | } | ||
| 29 | |||
| 30 | // Return current value | ||
| 31 | return Object.hasOwnProperty.call(globals.storage.variables, key) ? globals.storage.variables[key] : undefined ; |
||
| 32 | }; | ||
| 33 | }; | ||
| 34 | |||
| 35 | 1 | module.exports = [{ | |
| 36 | name: 'decrementVar', | ||
| 37 | factory: factory, | ||
| 38 | }]; | ||
| 39 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Path = require('path'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function(path) { | ||
| 7 | if ( !this['partial'] ) { |
||
| 8 | return; | ||
| 9 | } | ||
| 10 | |||
| 11 | // prevent access to __proto__ | ||
| 12 | // or any hidden object properties | ||
| 13 | 9 | path = path.replace('__', ''); | |
| 14 | |||
| 15 | // We don't want a slash as a prefix | ||
| 16 | if ( path[0] === '/' ) { |
||
| 17 | path = path.substr(1); | ||
| 18 | } | ||
| 19 | |||
| 20 | 9 | path = Path.join(path, this['partial']); | |
| 21 | |||
| 22 | 9 | if (globals.handlebars.partials[path] && Object.hasOwnProperty.call(globals.handlebars.partials, path)) { | |
| 23 | 3 | return globals.handlebars.partials[path](this); | |
| 24 | } | ||
| 25 | }; | ||
| 26 | }; | ||
| 27 | |||
| 28 | 1 | module.exports = [{ | |
| 29 | name: 'dynamicComponent', | ||
| 30 | factory: factory, | ||
| 31 | }]; | ||
| 32 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const {addResourceHint} = require('./lib/resourceHints'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function (href, rel) { | ||
| 7 | const options = arguments[arguments.length - 1]; | ||
| 8 | |||
| 9 | 2 | let cors, as = undefined; | |
| 10 | |||
| 11 | if ( options && options.hash) { |
||
| 12 | 2 | cors = options.hash.crossorigin; | |
| 13 | 2 | as = options.hash.as; | |
| 14 | } | ||
| 15 | |||
| 16 | 2 | try { | |
| 17 | 2 | return addResourceHint( | |
| 18 | globals, | ||
| 19 | href, | ||
| 20 | rel, | ||
| 21 | as, | ||
| 22 | cors | ||
| 23 | ); | ||
| 24 | } catch (e) { | ||
| 25 | console.error(`Early hint generation failed for path [${href}]`, e); | ||
| 26 | throw e; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | 1 | module.exports = [{ | |
| 32 | name: 'earlyHint', | ||
| 33 | factory | ||
| 34 | }]; | ||
| 35 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const he = require('he'); | |
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const common = require('./lib/common.js'); | |
| 5 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 6 | |||
| 7 | 1 | const factory = globals => { | |
| 8 | return function (string) { | ||
| 9 | string = common.unwrapIfSafeString(globals.handlebars, string); | ||
| 10 | 15 | if (!utils.isString(string)) { | |
| 11 | 1 | throw new ValidationError("Non-string passed to encodeHtmlEntities"); | |
| 12 | } | ||
| 13 | |||
| 14 | 14 | const options = arguments[arguments.length - 1]; | |
| 15 | |||
| 16 | 14 | let args = {}; | |
| 17 | |||
| 18 | if ( utils.isOptions(options) ) { |
||
| 19 | 14 | args = options.hash; | |
| 20 | |||
| 21 | // Whitelist of allowed named arguments into `he` function | ||
| 22 | 14 | const allowedArguments = [ | |
| 23 | 'useNamedReferences', | ||
| 24 | 'decimal', | ||
| 25 | 'encodeEverything', | ||
| 26 | 'allowUnsafeSymbols' | ||
| 27 | ]; | ||
| 28 | |||
| 29 | // Make sure all named arguments from options hash are in the whitelist and have boolean (string) values | ||
| 30 | 14 | if (Object.keys(args).some(key => !allowedArguments.includes(key)) | |
| 31 | || !Object.keys(args).map(key => args[key]).every(val => ['true', 'false'].includes(val))) { | ||
| 32 | 2 | throw new ValidationError("Invalid named argument passed to encodeHtmlEntities"); | |
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | 12 | return new globals.handlebars.SafeString(he.encode(string, args)); | |
| 37 | }; | ||
| 38 | }; | ||
| 39 | |||
| 40 | 1 | module.exports = [{ | |
| 41 | name: 'encodeHtmlEntities', | ||
| 42 | factory: factory, | ||
| 43 | }]; | ||
| 44 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | |||
| 5 | 1 | const factory = () => { | |
| 6 | return function (from, to, context) { | ||
| 7 | const options = arguments[arguments.length - 1]; | ||
| 8 | 23 | const maxIterations = 100; | |
| 9 | 23 | var output = ''; | |
| 10 | |||
| 11 | 23 | if (utils.isOptions(to)) { | |
| 12 | 1 | context = {}; | |
| 13 | 1 | to = from; | |
| 14 | 1 | from = 1; | |
| 15 | |||
| 16 | 22 | } else if (utils.isOptions(context)) { | |
| 17 | 15 | if (utils.isObject(to)) { | |
| 18 | 2 | context = to; | |
| 19 | 2 | to = from; | |
| 20 | 2 | from = 1; | |
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | 23 | if (to < from) { | |
| 25 | 1 | return; | |
| 26 | } | ||
| 27 | |||
| 28 | 22 | from = parseInt(from, 10); | |
| 29 | 22 | to = parseInt(to, 10); | |
| 30 | |||
| 31 | 22 | if ((to - from) >= maxIterations) { | |
| 32 | 2 | to = from + maxIterations - 1; | |
| 33 | } | ||
| 34 | |||
| 35 | 22 | for (var i = from; i <= to; i++) { | |
| 36 | 759 | context.$index = i; | |
| 37 | 759 | output += options.fn(context); | |
| 38 | } | ||
| 39 | |||
| 40 | 21 | return output; | |
| 41 | }; | ||
| 42 | }; | ||
| 43 | |||
| 44 | 1 | module.exports = [{ | |
| 45 | name: 'for', | ||
| 46 | factory: factory, | ||
| 47 | }]; | ||
| 48 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const { getValue } = require('./lib/common'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Based on https://github.com/helpers/handlebars-helpers/blob/0.9.0/lib/object.js#L128-L134 | ||
| 7 | * | ||
| 8 | * Get a value from the given context object. Property paths (`a.b.c`) may be used | ||
| 9 | * to get nested properties. | ||
| 10 | */ | ||
| 11 | 1 | const factory = (globals) => { | |
| 12 | return function (path, context) { | ||
| 13 | let options = arguments[arguments.length - 1]; | ||
| 14 | |||
| 15 | // for backwards compatibility | ||
| 16 | if (context === null || (typeof context !== 'object' && typeof context !== 'function' )) { |
||
| 17 | 5 | return context; | |
| 18 | } | ||
| 19 | |||
| 20 | // use an empty context if none was given | ||
| 21 | if ( !context ) { |
||
| 22 | context = {}; | ||
| 23 | } | ||
| 24 | |||
| 25 | // for backwards compatibility: safely use options hash as context if no context was passed | ||
| 26 | 11 | if (arguments.length < 3) { | |
| 27 | 1 | context = { hash: options.hash }; | |
| 28 | } | ||
| 29 | |||
| 30 | 11 | let value = getValue(context, path, globals); | |
| 31 | if ( options && options.fn) { |
||
| 32 | return value ? options.fn(value) : options.inverse(context) ; |
||
| 33 | } | ||
| 34 | 10 | return value; | |
| 35 | }; | ||
| 36 | }; | ||
| 37 | |||
| 38 | 1 | module.exports = [{ | |
| 39 | name: 'get', | ||
| 40 | factory: factory, | ||
| 41 | }]; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { getObjectStorageImage } = require('./lib/getObjectStorageImage') | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(path) { | ||
| 6 | const siteSettings = globals.getSiteSettings(); | ||
| 7 | |||
| 8 | 17 | const cdnUrl = siteSettings.cdn_url || ''; | |
| 9 | |||
| 10 | 17 | const options = arguments[arguments.length - 1]; | |
| 11 | |||
| 12 | 17 | return getObjectStorageImage(globals.handlebars, cdnUrl, 'content', path, options); | |
| 13 | }; | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | module.exports = [{ | |
| 17 | name: 'getContentImage', | ||
| 18 | factory: factory, | ||
| 19 | }]; | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { getObjectStorageImageSrcset } = require('./lib/getObjectStorageImage') | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(path) { | ||
| 6 | const siteSettings = globals.getSiteSettings(); | ||
| 7 | |||
| 8 | 7 | const cdnUrl = siteSettings.cdn_url || ''; | |
| 9 | 7 | const options = arguments[arguments.length - 1]; | |
| 10 | |||
| 11 | 7 | return getObjectStorageImageSrcset(globals.handlebars, cdnUrl, 'content', path, options); | |
| 12 | }; | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | module.exports = [{ | |
| 16 | name: 'getContentImageSrcset', | ||
| 17 | factory: factory, | ||
| 18 | }]; | ||
| 19 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const getFonts = require('./lib/fonts'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function() { | ||
| 7 | const fontConfig = getFonts('webFontLoaderConfig', globals.getThemeSettings(), globals.handlebars); | ||
| 8 | 1 | return new globals.handlebars.SafeString(JSON.stringify(fontConfig)); | |
| 9 | }; | ||
| 10 | }; | ||
| 11 | |||
| 12 | 1 | module.exports = [{ | |
| 13 | name: 'getFontLoaderConfig', | ||
| 14 | factory: factory, | ||
| 15 | }]; | ||
| 16 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const getFonts = require('./lib/fonts'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function () { | ||
| 7 | const options = arguments[arguments.length - 1]; | ||
| 8 | 6 | const fontDisplay = options.hash['font-display']; | |
| 9 | 6 | const getFontsOptions = {globals, fontDisplay}; | |
| 10 | 6 | return getFonts('linkElements', globals.getThemeSettings(), globals.handlebars, getFontsOptions); | |
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | 1 | module.exports = [{ | |
| 15 | name: 'getFontsCollection', | ||
| 16 | factory: factory, | ||
| 17 | }]; | ||
| 18 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const common = require('./lib/common.js'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function (image, presetName, defaultImageUrl) { | ||
| 8 | var sizeRegex = /^(\d+?)x(\d+?)$/g; | ||
| 9 | var settings = globals.getThemeSettings() || {} ; |
||
| 10 | 21 | var presets = settings._images; | |
| 11 | 21 | var size; | |
| 12 | 21 | var width; | |
| 13 | 21 | var height; | |
| 14 | |||
| 15 | // Get options object to access hash parameters | ||
| 16 | 21 | const options = arguments[arguments.length - 1]; | |
| 17 | const lossy = options && options.hash && options.hash.lossy; |
||
| 18 | |||
| 19 | if (!utils.isObject(image) || !utils.isString(image.data) |
||
| 20 | || !common.isValidURL(image.data) || image.data.indexOf('{:size}') === -1) { |
||
| 21 | // return empty string if not a valid image object | ||
| 22 | 5 | defaultImageUrl = defaultImageUrl ? defaultImageUrl : ''; | |
| 23 | 5 | return utils.isString(image) ? image : defaultImageUrl; | |
| 24 | } | ||
| 25 | |||
| 26 | 16 | if (utils.isObject(presets) && utils.isObject(presets[presetName])) { | |
| 27 | // If preset is one of the given presets in _images | ||
| 28 | 11 | width = parseInt(presets[presetName].width, 10) || common.maximumPixelSize; | |
| 29 | 11 | height = parseInt(presets[presetName].height, 10) || common.maximumPixelSize; | |
| 30 | 11 | size = `${width}x${height}`; | |
| 31 | 5 | } else if (sizeRegex.test(settings[presetName])) { | |
| 32 | // If preset name is a setting and match the NNNxNNN format | ||
| 33 | 3 | size = settings[presetName]; | |
| 34 | 3 | width = parseInt(size.split('x')[0], 10); | |
| 35 | 3 | height = parseInt(size.split('x')[1], 10); | |
| 36 | } else { | ||
| 37 | // Use the original image size | ||
| 38 | 2 | size = 'original'; | |
| 39 | } | ||
| 40 | |||
| 41 | if (Number.isInteger(image.width) && Number.isInteger(image.height) |
||
| 42 | && Number.isInteger(width) && Number.isInteger(height)) { |
||
| 43 | 2 | size = `${Math.min(image.width, width)}x${Math.min(image.height, height)}` | |
| 44 | } | ||
| 45 | |||
| 46 | 16 | const processedUrl = image.data.replace('{:size}', size); | |
| 47 | 16 | const finalUrl = common.appendLossyParam(processedUrl, lossy); | |
| 48 | |||
| 49 | 16 | return new globals.handlebars.SafeString(finalUrl); | |
| 50 | }; | ||
| 51 | }; | ||
| 52 | |||
| 53 | 1 | module.exports = [{ | |
| 54 | name: 'getImage', | ||
| 55 | factory: factory, | ||
| 56 | }]; | ||
| 57 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { getObjectStorageImage } = require('./lib/getObjectStorageImage') | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(path) { | ||
| 6 | const siteSettings = globals.getSiteSettings(); | ||
| 7 | |||
| 8 | 17 | const cdnUrl = siteSettings.cdn_url || ''; | |
| 9 | |||
| 10 | 17 | const options = arguments[arguments.length - 1]; | |
| 11 | |||
| 12 | 17 | return getObjectStorageImage(globals.handlebars, cdnUrl, 'image-manager', path, options); | |
| 13 | }; | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | module.exports = [{ | |
| 17 | name: 'getImageManagerImage', | ||
| 18 | factory: factory, | ||
| 19 | }]; | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { getObjectStorageImageSrcset } = require('./lib/getObjectStorageImage') | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(path) { | ||
| 6 | const siteSettings = globals.getSiteSettings(); | ||
| 7 | |||
| 8 | 7 | const cdnUrl = siteSettings.cdn_url || ''; | |
| 9 | 7 | const options = arguments[arguments.length - 1]; | |
| 10 | |||
| 11 | 7 | return getObjectStorageImageSrcset(globals.handlebars, cdnUrl, 'image-manager', path, options); | |
| 12 | }; | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | module.exports = [{ | |
| 16 | name: 'getImageManagerImageSrcset', | ||
| 17 | factory: factory, | ||
| 18 | }]; | ||
| 19 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const common = require('./lib/common'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function (...args) { | ||
| 7 | args.pop(); | ||
| 8 | 22 | let [image, defaultImageUrl] = args; | |
| 9 | // Regex to test size string is of the form 123x123 or 100w | ||
| 10 | 22 | const sizeRegex = /(^\d+w$)|(^(\d+?)x(\d+?)$)/; | |
| 11 | // Regex to test to that srcset descriptor is of the form 1x 1.5x 2x OR 123w | ||
| 12 | 22 | const descriptorRegex = /(^\d+w$)|(^([0-9](\.[0-9]+)?)x)$/; | |
| 13 | |||
| 14 | 22 | const options = arguments[arguments.length - 1]; | |
| 15 | const lossy = options && options.hash && options.hash.lossy; |
||
| 16 | |||
| 17 | 22 | if (utils.isUndefined(defaultImageUrl)) { | |
| 18 | 20 | defaultImageUrl = ''; | |
| 19 | } | ||
| 20 | |||
| 21 | if (!utils.isObject(image) || !utils.isString(image.data) |
||
| 22 | || !common.isValidURL(image.data) || image.data.indexOf('{:size}') === -1) { |
||
| 23 | // return empty string if not a valid image object | ||
| 24 | 6 | defaultImageUrl = defaultImageUrl ? defaultImageUrl : ''; | |
| 25 | 6 | return utils.isString(image) ? image : defaultImageUrl; | |
| 26 | } | ||
| 27 | |||
| 28 | 16 | let srcsets = {}; | |
| 29 | |||
| 30 | 16 | if (options.hash['use_default_sizes']) { | |
| 31 | 2 | if (Number.isInteger(image.width) && Number.isInteger(image.height)) { | |
| 32 | /* If we know the image dimensions, don't generate srcset sizes larger than the image */ | ||
| 33 | 1 | srcsets[`${image.width}w`] = `${image.width}w`; | |
| 34 | 1 | const defaultSrcsetSizes = [2560, 1920, 1280, 960, 640, 320, 160, 80]; | |
| 35 | 1 | defaultSrcsetSizes.forEach(width => { | |
| 36 | if (width < image.width) { | ||
| 37 | 6 | srcsets[`${width}w`] = `${width}w`; | |
| 38 | } | ||
| 39 | }); | ||
| 40 | } else { | ||
| 41 | /* If we DON'T know the image dimensions, generate a default set of srcsets | ||
| 42 | * This will upsize images */ | ||
| 43 | 1 | srcsets = { | |
| 44 | '2560w': '2560w', | ||
| 45 | '1920w': '1920w', | ||
| 46 | '1280w': '1280w', | ||
| 47 | '960w': '960w', | ||
| 48 | '640w': '640w', | ||
| 49 | '320w': '320w', | ||
| 50 | '160w': '160w', | ||
| 51 | '80w': '80w', | ||
| 52 | }; | ||
| 53 | } | ||
| 54 | } else { | ||
| 55 | 14 | srcsets = Object.assign({}, options.hash); | |
| 56 | // Remove non-srcset parameters like 'lossy' | ||
| 57 | 14 | delete srcsets.lossy; | |
| 58 | |||
| 59 | if ( !utils.isObject(srcsets) || Object.keys(srcsets).some(descriptor => { |
||
| 60 | return !(descriptorRegex.test(descriptor) && sizeRegex.test(srcsets[descriptor])); | ||
| 61 | })) { | ||
| 62 | // return empty string if not valid srcset object | ||
| 63 | 2 | return '' | |
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | // If there's only one argument, return a `src` only (also works for `srcset`) | ||
| 68 | 14 | if (Object.keys(srcsets).length === 1) { | |
| 69 | 6 | const processedUrl = image.data.replace('{:size}', srcsets[Object.keys(srcsets)[0]]); | |
| 70 | 6 | const finalUrl = common.appendLossyParam(processedUrl, lossy); | |
| 71 | 6 | return new globals.handlebars.SafeString(finalUrl); | |
| 72 | } | ||
| 73 | |||
| 74 | 8 | return new globals.handlebars.SafeString(Object.keys(srcsets).reverse().map(descriptor => { | |
| 75 | const processedUrl = image.data.replace('{:size}', srcsets[descriptor]); | ||
| 76 | 29 | const finalUrl = common.appendLossyParam(processedUrl, lossy); | |
| 77 | 29 | return ([finalUrl, descriptor].join(' ')); | |
| 78 | }).join(', ')); | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | |||
| 82 | 1 | module.exports = [{ | |
| 83 | name: 'getImageSrcset', | ||
| 84 | factory: factory, | ||
| 85 | }]; | ||
| 86 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const common = require('./lib/common'); | |
| 4 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function (image, size1x) { | ||
| 8 | // Regex to test size string is of the form 123x123 | ||
| 9 | const pixelDimensionsRegex = /(^\d+w$)|(^(\d+?)x(\d+?)$)/; | ||
| 10 | 13 | const options = arguments[arguments.length - 1]; | |
| 11 | const lossy = options && options.hash && options.hash.lossy; |
||
| 12 | |||
| 13 | 13 | const return1x = (image, size1x) => { | |
| 14 | const processedUrl = image.data.replace('{:size}', size1x); | ||
| 15 | 5 | const finalUrl = common.appendLossyParam(processedUrl, lossy); | |
| 16 | 5 | return new globals.handlebars.SafeString(finalUrl); | |
| 17 | }; | ||
| 18 | |||
| 19 | if (!utils.isObject(image) || !utils.isString(image.data) |
||
| 20 | || !common.isValidURL(image.data) || image.data.indexOf('{:size}') === -1) { |
||
| 21 | 2 | throw new ValidationError("Invalid StencilImage passed to getImageSrcset1x2x") | |
| 22 | } | ||
| 23 | |||
| 24 | if ( !pixelDimensionsRegex.test(size1x) ) { |
||
| 25 | throw new ValidationError("Invalid size argument passed to getImageSrcset1x2x, must be a set of pixel dimensions of the format '123x123'") | ||
| 26 | } | ||
| 27 | |||
| 28 | if (!image.width || !image.height || !Number.isInteger(image.width) || !Number.isInteger(image.height)) { |
||
| 29 | 2 | return return1x(image, size1x); | |
| 30 | } | ||
| 31 | |||
| 32 | 9 | const [width1x, height1x] = size1x.split('x').map(i => parseInt(i)); | |
| 33 | |||
| 34 | if ( width1x > image.width |
||
| 35 | || width1x > image.height) { | ||
| 36 | // Either the image is too small to make a srcset with a 2x size, | ||
| 37 | // or those sizes would be larger than the resizer supports | ||
| 38 | 2 | return return1x(image, size1x); | |
| 39 | } else { | ||
| 40 | 7 | const smallestFactor = Math.min((image.width / width1x), (image.height / height1x)); | |
| 41 | 7 | const factor = smallestFactor < 2 ? smallestFactor : 2; | |
| 42 | 7 | const roundedFactor = +(factor).toFixed(4) //cast to Number for clean rounding | |
| 43 | |||
| 44 | 7 | const [widthXx, heightXx] = [width1x, height1x].map(i => Math.round(i * factor)); | |
| 45 | |||
| 46 | 7 | if (widthXx > common.maximumPixelSize || heightXx > common.maximumPixelSize) { | |
| 47 | 1 | return return1x(image, size1x); | |
| 48 | } | ||
| 49 | |||
| 50 | 6 | const sizeXx = `${widthXx}x${heightXx}`; | |
| 51 | |||
| 52 | 6 | const url1x = common.appendLossyParam(image.data.replace('{:size}', size1x), lossy); | |
| 53 | 6 | const urlXx = common.appendLossyParam(image.data.replace('{:size}', sizeXx), lossy); | |
| 54 | |||
| 55 | 6 | return new globals.handlebars.SafeString(`${url1x} 1x, ${urlXx} ${roundedFactor}x`); | |
| 56 | } | ||
| 57 | }; | ||
| 58 | }; | ||
| 59 | |||
| 60 | 1 | module.exports = [{ | |
| 61 | name: 'getImageSrcset1x2x', | ||
| 62 | factory: factory, | ||
| 63 | }]; | ||
| 64 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const { getValue } = require('./lib/common'); | |
| 4 | |||
| 5 | /* | ||
| 6 | * Based on https://github.com/helpers/handlebars-helpers/blob/0.9.0/lib/object.js#L149-L151 | ||
| 7 | * and https://github.com/jonschlinkert/get-object/blob/0.2.0/index.js#L12-L24 | ||
| 8 | * | ||
| 9 | * Get an object or array containing a value from the given context object. | ||
| 10 | * Property paths (`a.b.c`) may be used to get nested properties. | ||
| 11 | */ | ||
| 12 | 1 | const factory = (globals) => { | |
| 13 | return function (...args) { | ||
| 14 | args.pop(); | ||
| 15 | 5 | let [path, context] = args; | |
| 16 | // use an empty context if none was given | ||
| 17 | // (expect 3 args: `path`, `context`, and the `options` object | ||
| 18 | // Handlebars always passes as the last argument to a helper) | ||
| 19 | if ( arguments.length < 2 ) { |
||
| 20 | context = {}; | ||
| 21 | } | ||
| 22 | |||
| 23 | if ( !path ) { |
||
| 24 | // return entire context object if no property/path specified | ||
| 25 | return context; | ||
| 26 | } | ||
| 27 | |||
| 28 | 5 | path = String(path); | |
| 29 | |||
| 30 | 5 | const parts = path.split(/[[.\]]/).filter(Boolean); | |
| 31 | |||
| 32 | 5 | let value = getValue(context, parts, globals); | |
| 33 | |||
| 34 | // for backwards compatibility: `get-object` returns on empty object instead of | ||
| 35 | // getting props with 'false' values (not just undefined) | ||
| 36 | 5 | if (!value) { | |
| 37 | 1 | return {}; | |
| 38 | } | ||
| 39 | |||
| 40 | // return an array if the final path part is numeric to mimic behavior of `get-object` | ||
| 41 | 4 | const last = parts[parts.length - 1]; | |
| 42 | 4 | if (Number.isFinite(Number(last))) { | |
| 43 | 1 | return [ value ]; | |
| 44 | } | ||
| 45 | 3 | let result = {}; | |
| 46 | 3 | result[last] = value | |
| 47 | 3 | return result; | |
| 48 | }; | ||
| 49 | }; | ||
| 50 | |||
| 51 | 1 | module.exports = [{ | |
| 52 | name: 'getObject', | ||
| 53 | factory: factory, | ||
| 54 | }]; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function (key) { | ||
| 8 | if (!utils.isString(key)) { | ||
| 9 | 1 | throw new ValidationError("getVar helper key must be a string"); | |
| 10 | } | ||
| 11 | |||
| 12 | 194 | return globals.storage.variables && Object.hasOwnProperty.call(globals.storage.variables, key) ? globals.storage.variables[key] : undefined | |
| 13 | }; | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | module.exports = [{ | |
| 17 | name: 'getVar', | ||
| 18 | factory: factory, | ||
| 19 | }]; | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = () => { | |
| 4 | return function() { | ||
| 5 | return undefined; | ||
| 6 | }; | ||
| 7 | }; | ||
| 8 | |||
| 9 | 1 | module.exports = [{ | |
| 10 | name: 'helperMissing', | ||
| 11 | factory: factory, | ||
| 12 | }]; | ||
| 13 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function (lvalue, operator, rvalue) { | ||
| 8 | const options = arguments[arguments.length - 1]; | ||
| 9 | 79 | let result; | |
| 10 | |||
| 11 | // Only parameter | ||
| 12 | 79 | if (utils.isOptions(operator)) { | |
| 13 | // If an array is passed as the only parameter | ||
| 14 | 21 | if (utils.isArray(lvalue)) { | |
| 15 | 3 | result = !!lvalue.length; | |
| 16 | } | ||
| 17 | // If an empty object is passed, treat as false | ||
| 18 | 18 | else if (utils.isEmpty(lvalue) && utils.isObject(lvalue)) { | |
| 19 | 1 | result = false; | |
| 20 | } | ||
| 21 | // Everything else | ||
| 22 | else { | ||
| 23 | 17 | result = !!lvalue; | |
| 24 | } | ||
| 25 | } else { | ||
| 26 | |||
| 27 | 58 | if (utils.isOptions(rvalue)) { | |
| 28 | // @TODO: this block is for backwards compatibility with 'compare' helper | ||
| 29 | // Remove after operator='==' is removed from stencil theme | ||
| 30 | 20 | rvalue = operator; | |
| 31 | operator = options.hash.operator || "==" ; |
||
| 32 | } | ||
| 33 | |||
| 34 | 58 | switch (operator) { | |
| 35 | case '==': | ||
| 36 | 4 | result = (lvalue == rvalue); | |
| 37 | 4 | break; | |
| 38 | |||
| 39 | case '===': | ||
| 40 | 8 | result = (lvalue === rvalue); | |
| 41 | 8 | break; | |
| 42 | |||
| 43 | case '!=': | ||
| 44 | 4 | result = (lvalue != rvalue); | |
| 45 | 4 | break; | |
| 46 | |||
| 47 | case '!==': | ||
| 48 | 6 | result = (lvalue !== rvalue); | |
| 49 | 6 | break; | |
| 50 | |||
| 51 | case '<': | ||
| 52 | 7 | result = (lvalue < rvalue); | |
| 53 | 7 | break; | |
| 54 | |||
| 55 | case '>': | ||
| 56 | 4 | result = (lvalue > rvalue); | |
| 57 | 4 | break; | |
| 58 | |||
| 59 | case '<=': | ||
| 60 | 4 | result = (lvalue <= rvalue); | |
| 61 | 4 | break; | |
| 62 | |||
| 63 | case '>=': | ||
| 64 | 4 | result = (lvalue >= rvalue); | |
| 65 | 4 | break; | |
| 66 | |||
| 67 | case 'gtnum': | ||
| 68 | 9 | if (typeof lvalue === 'string' && typeof (rvalue) === 'string' && !isNaN(lvalue) && !isNaN(rvalue)) { | |
| 69 | 3 | result = parseInt(lvalue) > parseInt(rvalue); | |
| 70 | } else { | ||
| 71 | 6 | throw new ValidationError("if gtnum only accepts numbers (as strings)"); | |
| 72 | } | ||
| 73 | 3 | break; | |
| 74 | |||
| 75 | case 'typeof': | ||
| 76 | 8 | result = (typeof lvalue === rvalue); | |
| 77 | 8 | break; | |
| 78 | |||
| 79 | default: | ||
| 80 | throw new ValidationError("Handlerbars Helper 'if' doesn't know the operator " + operator); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | 73 | if (!options.fn || !options.inverse) { | |
| 85 | 2 | options.fn = () => true; | |
| 86 | 2 | options.inverse = () => false; | |
| 87 | } | ||
| 88 | |||
| 89 | 73 | if (result) { | |
| 90 | 36 | return options.fn(this); | |
| 91 | } else { | ||
| 92 | 37 | return options.inverse(this); | |
| 93 | } | ||
| 94 | }; | ||
| 95 | }; | ||
| 96 | |||
| 97 | 1 | module.exports = [{ | |
| 98 | name: 'if', | ||
| 99 | factory: factory, | ||
| 100 | }]; | ||
| 101 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 4 | |||
| 5 | 1 | const max_keys = 50; | |
| 6 | |||
| 7 | 1 | const factory = globals => { | |
| 8 | return function (key) { | ||
| 9 | if (!utils.isString(key)) { | ||
| 10 | 1 | throw new ValidationError("incrementVar helper key must be a string"); | |
| 11 | } | ||
| 12 | |||
| 13 | // Setup storage | ||
| 14 | 9 | if (typeof globals.storage.variables === 'undefined') { | |
| 15 | 3 | globals.storage.variables = Object.create(null); | |
| 16 | } | ||
| 17 | |||
| 18 | 9 | if (Object.hasOwnProperty.call(globals.storage.variables, key) && Number.isInteger(globals.storage.variables[key])) { | |
| 19 | // Increment value if it already exists | ||
| 20 | 5 | globals.storage.variables[key] += 1; | |
| 21 | } else { | ||
| 22 | // Make sure the number of total keys is within the limit | ||
| 23 | if ( Object.keys(globals.storage.variables).length >= max_keys ) { |
||
| 24 | throw new ValidationError(`Unique keys in variable storage may not exceed ${max_keys} in total`); | ||
| 25 | } | ||
| 26 | // Initialize or re-initialize value | ||
| 27 | 4 | globals.storage.variables[key] = 0; | |
| 28 | } | ||
| 29 | |||
| 30 | // Return current value | ||
| 31 | return Object.hasOwnProperty.call(globals.storage.variables, key) ? globals.storage.variables[key] : undefined ; |
||
| 32 | }; | ||
| 33 | }; | ||
| 34 | |||
| 35 | 1 | module.exports = [{ | |
| 36 | name: 'incrementVar', | ||
| 37 | factory: factory, | ||
| 38 | }]; | ||
| 39 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function(key, value) { | ||
| 5 | if ( typeof value === 'function' ) { |
||
| 6 | return; | ||
| 7 | } | ||
| 8 | |||
| 9 | // Setup storage | ||
| 10 | 2 | if (typeof globals.storage.inject === 'undefined') { | |
| 11 | 1 | globals.storage.inject = Object.create(null); | |
| 12 | } | ||
| 13 | |||
| 14 | // Store value for later use by jsContext | ||
| 15 | 2 | globals.storage.inject[key] = value; | |
| 16 | }; | ||
| 17 | }; | ||
| 18 | |||
| 19 | 1 | module.exports = [{ | |
| 20 | name: 'inject', | ||
| 21 | factory: factory, | ||
| 22 | }]; | ||
| 23 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = () => { | |
| 4 | return function(array, separator) { | ||
| 5 | const options = arguments[arguments.length - 1]; | ||
| 6 | var config = options.hash || {} ; |
||
| 7 | |||
| 8 | if ( !Array.isArray(array) ) { |
||
| 9 | throw new TypeError("Non-array passed to join helper"); | ||
| 10 | } | ||
| 11 | 5 | array = array.slice(); | |
| 12 | |||
| 13 | // Truncate array | ||
| 14 | 5 | if (config.limit && array.length > config.limit) { | |
| 15 | 2 | array = array.slice(0, config.limit); | |
| 16 | } | ||
| 17 | |||
| 18 | // Use lastSeparator between last and second last item, if provided | ||
| 19 | 5 | if (config.lastSeparator) { | |
| 20 | 2 | var truncatedArray = array.slice(0, -1), | |
| 21 | lastItem = array.slice(-1); | ||
| 22 | |||
| 23 | 2 | return truncatedArray.join(separator) + config.lastSeparator + lastItem; | |
| 24 | } | ||
| 25 | |||
| 26 | 3 | return array.join(separator); | |
| 27 | }; | ||
| 28 | }; | ||
| 29 | |||
| 30 | 1 | module.exports = [{ | |
| 31 | name: 'join', | ||
| 32 | factory: factory, | ||
| 33 | }]; | ||
| 34 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | // Used in conjunction with 'inject' | ||
| 4 | 1 | const factory = globals => { | |
| 5 | return function() { | ||
| 6 | const jsContext = JSON.stringify(JSON.stringify( globals.storage.inject || {} )); // TODO: why double stringify?? |
||
| 7 | 1 | return new globals.handlebars.SafeString(jsContext); | |
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | 1 | module.exports = [{ | |
| 12 | name: 'jsContext', | ||
| 13 | factory: factory, | ||
| 14 | }]; | ||
| 15 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const common = require('./lib/common.js'); | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(data) { | ||
| 6 | data = common.unwrapIfSafeString(globals.handlebars, data); | ||
| 7 | 7 | return JSON.stringify(data); | |
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | 1 | module.exports = [{ | |
| 12 | name: 'json', | ||
| 13 | factory: factory, | ||
| 14 | }]; | ||
| 15 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | function factory(globals) { | ||
| 4 | 677 | return function (value) { | |
| 5 | const options = arguments[arguments.length - 1]; | ||
| 6 | |||
| 7 | 9 | try { | |
| 8 | 9 | if (options.fn) { | |
| 9 | 3 | return options.fn(JSON.parse(value)); | |
| 10 | } | ||
| 11 | 6 | return JSON.parse(value); | |
| 12 | } catch (err) { | ||
| 13 | 4 | if (options.fn) { // If function block {{#JSONparseSafe "{}"}} escape here on excaption. | |
| 14 | 2 | return options.inverse(this); | |
| 15 | } | ||
| 16 | 2 | return undefined; // Escape hatch to avoid crash due to inline malformed JSON input. | |
| 17 | } | ||
| 18 | }; | ||
| 19 | } | ||
| 20 | |||
| 21 | 1 | module.exports = [{ | |
| 22 | name: 'JSONparseSafe', | ||
| 23 | factory: factory, | ||
| 24 | }]; | ||
| 25 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function(translationKey) { | ||
| 5 | const options = arguments[arguments.length - 1]; | ||
| 6 | 3 | const translator = globals.getTranslator(); | |
| 7 | 3 | if (translator) { | |
| 8 | 2 | const result = translator.translate(translationKey, options.hash); | |
| 9 | 2 | if (typeof result === 'string') { | |
| 10 | 1 | return result; | |
| 11 | } | ||
| 12 | } | ||
| 13 | 2 | return '' | |
| 14 | }; | ||
| 15 | }; | ||
| 16 | |||
| 17 | 1 | module.exports = [{ | |
| 18 | name: 'lang', | ||
| 19 | factory: factory, | ||
| 20 | }]; | ||
| 21 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function(keyFilter) { | ||
| 5 | const translator = globals.getTranslator(); | ||
| 6 | 2 | const langJson = translator ? translator.getLanguage(keyFilter) : {}; | |
| 7 | 2 | return JSON.stringify(langJson); | |
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | 1 | module.exports = [{ | |
| 12 | name: 'langJson', | ||
| 13 | factory: factory, | ||
| 14 | }]; | ||
| 15 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Limit an array to the second argument | ||
| 7 | * | ||
| 8 | * @example | ||
| 9 | * {{limit array 4}} | ||
| 10 | */ | ||
| 11 | 1 | const factory = () => { | |
| 12 | return function (data, limit) { | ||
| 13 | if (utils.isString(data)) { | ||
| 14 | 1 | return data.substring(0, limit); | |
| 15 | } | ||
| 16 | if ( !utils.isArray(data) ) { |
||
| 17 | return []; | ||
| 18 | } | ||
| 19 | |||
| 20 | 1 | return data.slice(0, limit); | |
| 21 | }; | ||
| 22 | }; | ||
| 23 | |||
| 24 | 1 | module.exports = [{ | |
| 25 | name: 'limit', | ||
| 26 | factory: factory, | ||
| 27 | }]; | ||
| 28 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const chrono = require('chrono-node'); | |
| 5 | |||
| 6 | // suppress error messages that are not actionable | ||
| 7 | 1 | const moment = require('moment'); | |
| 8 | 1 | moment.suppressDeprecationWarnings = true; | |
| 9 | |||
| 10 | /* | ||
| 11 | * Modified from https://github.com/helpers/helper-date/blob/1.0.1/index.js | ||
| 12 | */ | ||
| 13 | 1 | const factory = () => { | |
| 14 | return function (str, pattern) { | ||
| 15 | // always use the Handlebars-generated options object | ||
| 16 | let options = arguments[arguments.length - 1]; | ||
| 17 | |||
| 18 | // Ideally we'd check how many args were passed and set `str` and `pattern` to | ||
| 19 | // null if they weren't explicitly provided. However, doing so could break | ||
| 20 | // backwards compatibility since we previously depended on helper-date@0.2.3. | ||
| 21 | |||
| 22 | // if no args are passed, return a formatted date | ||
| 23 | /* eslint no-eq-null: ["off"] */ | ||
| 24 | 12 | if (str == null && pattern == null) { | |
| 25 | 2 | moment.locale('en'); | |
| 26 | 2 | return moment().format('MMMM DD, YYYY'); | |
| 27 | } | ||
| 28 | |||
| 29 | 10 | var defaults = { lang: 'en', date: new Date(str) }; | |
| 30 | 10 | var opts = utils.context(this, defaults, options); | |
| 31 | |||
| 32 | // set the language to use | ||
| 33 | moment.locale( opts.lang || opts.language ); |
||
| 34 | |||
| 35 | if ( opts.datejs === false ) { |
||
| 36 | return moment(new Date(str)).format(pattern); | ||
| 37 | } | ||
| 38 | |||
| 39 | // if both args are strings, this could apply to either lib. | ||
| 40 | // so instead of doing magic we'll just ask the user to tell | ||
| 41 | // us if the args should be passed to date.js or moment. | ||
| 42 | 10 | if (typeof str === 'string' && typeof pattern === 'string') { | |
| 43 | 6 | return moment(chrono.parseDate(str)).format(pattern); | |
| 44 | } | ||
| 45 | |||
| 46 | |||
| 47 | // If handlebars, expose moment methods as hash properties | ||
| 48 | if ( options && options.hash) { |
||
| 49 | if ( options.context ) { |
||
| 50 | options.hash = Object.assign({}, options.hash, options.context); | ||
| 51 | } | ||
| 52 | |||
| 53 | 4 | var res = moment(str); | |
| 54 | 4 | for (var key in options.hash) { | |
| 55 | // prevent access to prototype methods | ||
| 56 | 5 | if (Object.keys(moment.prototype).indexOf(key) !== -1 && typeof res[key] === 'function') { | |
| 57 | 4 | return res[key](options.hash[key]); | |
| 58 | } else { | ||
| 59 | 1 | console.warn('moment.js does not support "' + key + '"'); | |
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | if (utils.isObject(str)) { | ||
| 65 | return moment(str).format(pattern); | ||
| 66 | } | ||
| 67 | |||
| 68 | // if only a string is passed, assume it's a date pattern ('YYYY') | ||
| 69 | if (typeof str === 'string' && !pattern) { | ||
| 70 | return moment().format(str); | ||
| 71 | } | ||
| 72 | |||
| 73 | return moment(str).format(pattern); | ||
| 74 | }; | ||
| 75 | }; | ||
| 76 | |||
| 77 | 1 | module.exports = [{ | |
| 78 | name: "moment", | ||
| 79 | factory: factory, | ||
| 80 | }] |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 3 | |||
| 4 | /** | ||
| 5 | * Format numbers | ||
| 6 | * | ||
| 7 | * @param integer n: length of decimal | ||
| 8 | * @param mixed s: thousands delimiter | ||
| 9 | * @param mixed c: decimal delimiter | ||
| 10 | */ | ||
| 11 | function numberFormat(value, n, s, c) { | ||
| 12 | var re = '\\d(?=(\\d{3})+' + ( n > 0 ? '\\D' : '$' ) + ')', |
||
| 13 | num = value.toFixed(Math.max(0, ~~n)); | ||
| 14 | |||
| 15 | return ( c ? num.replace('.', c) : num ).replace(new RegExp(re, 'g'), '$&' + ( s || ',' )); |
||
| 16 | } | ||
| 17 | |||
| 18 | 1 | const factory = globals => { | |
| 19 | return function(...args) { | ||
| 20 | const siteSettings = globals.getSiteSettings(); | ||
| 21 | 9 | const money = siteSettings.money; | |
| 22 | |||
| 23 | // remove options hash object | ||
| 24 | 9 | args.pop(); | |
| 25 | |||
| 26 | 9 | let value = args[0]; | |
| 27 | |||
| 28 | 9 | if (isNaN(value)) { | |
| 29 | 1 | throw new ValidationError("money helper accepts only Number's as first parameter"); | |
| 30 | } | ||
| 31 | |||
| 32 | 8 | const decimalPlaces = args[1] || money.decimal_places; | |
| 33 | |||
| 34 | 8 | if (isNaN(decimalPlaces)) { | |
| 35 | 1 | throw new ValidationError("money helper accepts only Number's for decimal places"); | |
| 36 | } | ||
| 37 | 7 | const thousandsToken = args[2] || money.thousands_token; | |
| 38 | 7 | const decimalToken = args[3] || money.decimal_token; | |
| 39 | |||
| 40 | 7 | value = numberFormat( | |
| 41 | value, | ||
| 42 | decimalPlaces, | ||
| 43 | thousandsToken, | ||
| 44 | decimalToken | ||
| 45 | ); | ||
| 46 | |||
| 47 | return money.currency_location.toLowerCase() === 'left' |
||
| 48 | ? money.currency_token + ' ' + value | ||
| 49 | : value + ' ' + money.currency_token ; |
||
| 50 | }; | ||
| 51 | }; | ||
| 52 | |||
| 53 | 1 | module.exports = [{ | |
| 54 | name: 'money', | ||
| 55 | factory: factory, | ||
| 56 | }]; | ||
| 57 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Concats multi values, primarily used as a subhelper | ||
| 5 | * @example | ||
| 6 | * {{multiConcat "string1" "string2" "string3"}} | ||
| 7 | */ | ||
| 8 | |||
| 9 | |||
| 10 | 1 | const factory = globals => { | |
| 11 | return function(...args) { | ||
| 12 | // Take the last arg which is a Handlebars options object out of args array | ||
| 13 | args.pop(); | ||
| 14 | |||
| 15 | 313 | return args.join(''); | |
| 16 | }; | ||
| 17 | |||
| 18 | }; | ||
| 19 | |||
| 20 | |||
| 21 | 1 | module.exports = [{ | |
| 22 | name: 'multiConcat', | ||
| 23 | factory: factory, | ||
| 24 | }]; | ||
| 25 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | // https://github.com/danharper/Handlebars-Helpers/blob/master/src/helpers.js#L89 | ||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(text) { | ||
| 6 | var nl2br = (globals.handlebars.escapeExpression(text) + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br>' + '$2'); | ||
| 7 | 1 | return new globals.handlebars.SafeString(nl2br); | |
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | 1 | module.exports = [{ | |
| 12 | name: 'nl2br', | ||
| 13 | factory: factory, | ||
| 14 | }]; | ||
| 15 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function() { | ||
| 5 | const params = globals.getRequestParams(); | ||
| 6 | if ( params && params.security && params.security.nonce) { |
||
| 7 | 1 | return new globals.handlebars.SafeString(params.security.nonce); | |
| 8 | } | ||
| 9 | 1 | return '' | |
| 10 | }; | ||
| 11 | }; | ||
| 12 | |||
| 13 | 1 | module.exports = [{ | |
| 14 | name: 'nonce', | ||
| 15 | factory: factory, | ||
| 16 | }]; | ||
| 17 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Based on https://github.com/helpers/handlebars-helpers/blob/0.8.4/lib/string.js#L221-L233 | ||
| 5 | * | ||
| 6 | * Return the number of occurrences of `substring` within the | ||
| 7 | * given `string`. | ||
| 8 | * | ||
| 9 | * ```handlebars | ||
| 10 | * {{occurrences "foo bar foo bar baz" "foo"}} | ||
| 11 | * //=> 2 | ||
| 12 | * ``` | ||
| 13 | * @param {String} `str` | ||
| 14 | * @param {String} `substring` | ||
| 15 | * @return {Number} Number of occurrences | ||
| 16 | */ | ||
| 17 | |||
| 18 | 1 | const factory = () => { | |
| 19 | return function(str, substring) { | ||
| 20 | if (typeof str !== 'string' || str.length === 0) { | ||
| 21 | 4 | return 0; | |
| 22 | } | ||
| 23 | |||
| 24 | 8 | if (typeof substring !== 'string' || substring.length === 0) { | |
| 25 | 4 | return 0; | |
| 26 | } | ||
| 27 | |||
| 28 | 4 | const len = substring.length; | |
| 29 | 4 | let pos = 0; | |
| 30 | 4 | let numOccurrences = 0; | |
| 31 | |||
| 32 | 4 | while ((pos = str.indexOf(substring, pos)) > -1) { | |
| 33 | 6 | pos += len; | |
| 34 | 6 | numOccurrences++; | |
| 35 | } | ||
| 36 | |||
| 37 | 4 | return numOccurrences; | |
| 38 | }; | ||
| 39 | }; | ||
| 40 | |||
| 41 | 1 | module.exports = [{ | |
| 42 | name: 'occurrences', | ||
| 43 | factory: factory, | ||
| 44 | }]; | ||
| 45 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const { getValue } = require('./lib/common'); | |
| 5 | |||
| 6 | /** | ||
| 7 | * Based on https://github.com/helpers/handlebars-helpers/blob/0.9.0/lib/misc.js#L26-L28 | ||
| 8 | * | ||
| 9 | * Get a value from the options object. Property paths (`a.b.c`) may be used | ||
| 10 | * to get nested properties. | ||
| 11 | */ | ||
| 12 | 1 | const factory = (globals) => { | |
| 13 | return function (path, locals) { | ||
| 14 | // preserve `option` behavior with missing args while ensuring the correct | ||
| 15 | // options object is used | ||
| 16 | let options = arguments[arguments.length - 1]; | ||
| 17 | if ( arguments.length < 3 ) { |
||
| 18 | 3 | locals = null; | |
| 19 | } | ||
| 20 | if ( arguments.length < 2 ) { |
||
| 21 | path = ''; | ||
| 22 | } | ||
| 23 | |||
| 24 | 3 | let opts = utils.options(this, locals, options); | |
| 25 | |||
| 26 | 3 | return getValue(opts, path, globals); | |
| 27 | }; | ||
| 28 | }; | ||
| 29 | |||
| 30 | 1 | module.exports = [{ | |
| 31 | name: "option", | ||
| 32 | factory: factory, | ||
| 33 | }] |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Yield block if any object within a collection matches supplied predicate | ||
| 7 | * | ||
| 8 | * @example | ||
| 9 | * {{#or 1 0 0 0 0 0}} ... {{/or}} | ||
| 10 | */ | ||
| 11 | 1 | const factory = () => { | |
| 12 | return function (...args) { | ||
| 13 | |||
| 14 | // Take the last arg which is a Handlebars options object out of args array | ||
| 15 | const opts = args.pop(); | ||
| 16 | |||
| 17 | // Evaluate all args in args array to see if any are truthy | ||
| 18 | 6 | const any = args.some(function (arg) { | |
| 19 | if (utils.isArray(arg)) { | ||
| 20 | 3 | return !!arg.length; | |
| 21 | } | ||
| 22 | // If an empty object is passed, arg is false | ||
| 23 | 8 | else if (utils.isEmpty(arg) && utils.isObject(arg)) { | |
| 24 | 1 | return false; | |
| 25 | } | ||
| 26 | // Everything else | ||
| 27 | else { | ||
| 28 | 7 | return !!arg; | |
| 29 | } | ||
| 30 | }); | ||
| 31 | |||
| 32 | 6 | if (any) { | |
| 33 | 4 | return opts.fn(this); | |
| 34 | } | ||
| 35 | |||
| 36 | 2 | return opts.inverse(this); | |
| 37 | }; | ||
| 38 | }; | ||
| 39 | |||
| 40 | 1 | module.exports = [{ | |
| 41 | name: 'or', | ||
| 42 | factory: factory, | ||
| 43 | }]; | ||
| 44 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const common = require('./lib/common.js'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function(name) { | ||
| 8 | name = common.unwrapIfSafeString(globals.handlebars, name); | ||
| 9 | 7 | if (!utils.isString(name)) { | |
| 10 | 3 | globals.getLogger().info("Non-string passed to partial helper"); | |
| 11 | 3 | return ''; | |
| 12 | } | ||
| 13 | |||
| 14 | 4 | if (Object.getOwnPropertyNames(Object.prototype).includes(name)) { | |
| 15 | 1 | globals.getLogger().info(`Invalid name '${name}' passed to the partial helper. Returning empty string.`); | |
| 16 | 1 | return ''; | |
| 17 | } | ||
| 18 | |||
| 19 | 3 | const options = arguments[arguments.length - 1]; | |
| 20 | 3 | globals.handlebars.registerPartial(name, options.fn); | |
| 21 | }; | ||
| 22 | }; | ||
| 23 | |||
| 24 | 1 | module.exports = [{ | |
| 25 | name: 'partial', | ||
| 26 | factory: factory, | ||
| 27 | }]; | ||
| 28 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = () => { | |
| 4 | return function(collection, path) { | ||
| 5 | if (collection && Array.isArray(collection)) { | ||
| 6 | 5 | return collection.map(item => Object.hasOwnProperty.call(item, path) ? item[path] : undefined); | |
| 7 | } | ||
| 8 | 2 | return []; | |
| 9 | }; | ||
| 10 | }; | ||
| 11 | |||
| 12 | 1 | module.exports = [{ | |
| 13 | name: 'pluck', | ||
| 14 | factory: factory, | ||
| 15 | }]; | ||
| 16 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | 'use restrict'; | |
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function(value) { | ||
| 5 | var string = JSON.stringify(value, null, 2); | ||
| 6 | |||
| 7 | 2 | string = globals.handlebars.Utils.escapeExpression(string); | |
| 8 | |||
| 9 | 2 | return '<pre>' + string + '</pre>'; | |
| 10 | }; | ||
| 11 | }; | ||
| 12 | |||
| 13 | 1 | module.exports = [{ | |
| 14 | name: 'pre', | ||
| 15 | factory: factory, | ||
| 16 | }]; | ||
| 17 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function(params) { | ||
| 5 | let regionId = params.hash.name; | ||
| 6 | 4 | let regionTranslation = params.hash.translation; | |
| 7 | 4 | let contentRegions = globals.getContent(); | |
| 8 | |||
| 9 | if ( !contentRegions ) { |
||
| 10 | return ''; | ||
| 11 | } | ||
| 12 | 4 | const translationDataAttribute = regionTranslation ? ` data-content-region-translation="${regionTranslation}"` : ''; | |
| 13 | |||
| 14 | 4 | const content = `<div data-content-region="${regionId}"${translationDataAttribute}>${contentRegions[regionId] || ''}</div>`; | |
| 15 | |||
| 16 | 4 | return new globals.handlebars.SafeString(content); | |
| 17 | }; | ||
| 18 | }; | ||
| 19 | |||
| 20 | 1 | module.exports = [{ | |
| 21 | name: 'region', | ||
| 22 | factory: factory, | ||
| 23 | }]; | ||
| 24 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const common = require('./lib/common.js'); | |
| 3 | |||
| 4 | 1 | const factory = globals => { | |
| 5 | return function(needle, haystack) { | ||
| 6 | needle = common.unwrapIfSafeString(globals.handlebars, needle); | ||
| 7 | 9 | haystack = common.unwrapIfSafeString(globals.handlebars, haystack); | |
| 8 | 9 | const options = arguments[arguments.length - 1]; | |
| 9 | |||
| 10 | 9 | if (typeof needle !== 'string') { | |
| 11 | 3 | return options.inverse(this); | |
| 12 | } | ||
| 13 | |||
| 14 | 6 | const regex = new RegExp(escapeRegex(needle), 'g'); | |
| 15 | |||
| 16 | // Yield block if true | ||
| 17 | 6 | if (typeof haystack === 'string' && regex.test(haystack)) { | |
| 18 | 5 | return haystack.replace(regex, options.fn(this)); | |
| 19 | } else { | ||
| 20 | 1 | return options.inverse(this); | |
| 21 | } | ||
| 22 | }; | ||
| 23 | }; | ||
| 24 | |||
| 25 | function escapeRegex(string) { | ||
| 26 | 6 | return string.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); | |
| 27 | } | ||
| 28 | |||
| 29 | 1 | module.exports = [{ | |
| 30 | name: 'replace', | ||
| 31 | factory: factory, | ||
| 32 | }]; | ||
| 33 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./3p/utils'); | |
| 4 | 1 | const getFonts = require('./lib/fonts'); | |
| 5 | 1 | const { | |
| 6 | addResourceHint, | ||
| 7 | resourceHintAllowedStates, | ||
| 8 | resourceHintAllowedTypes, | ||
| 9 | resourceHintAllowedCors | ||
| 10 | } = require('./lib/resourceHints'); | ||
| 11 | |||
| 12 | 1 | const fontResources = { | |
| 13 | 'Google': [ | ||
| 14 | 'https://fonts.googleapis.com', | ||
| 15 | 'https://fonts.gstatic.com', | ||
| 16 | ], | ||
| 17 | }; | ||
| 18 | |||
| 19 | function format(host) { | ||
| 20 | 2 | return `<link rel="dns-prefetch preconnect" href="${host}" crossorigin>`; | |
| 21 | } | ||
| 22 | |||
| 23 | 1 | const factory = globals => { | |
| 24 | return function () { | ||
| 25 | |||
| 26 | let hosts = []; | ||
| 27 | |||
| 28 | // Add cdn | ||
| 29 | 1 | const siteSettings = globals.getSiteSettings(); | |
| 30 | const cdnUrl = siteSettings['cdn_url'] || ''; |
||
| 31 | if ( utils.isString(cdnUrl) ) { |
||
| 32 | hosts.push(cdnUrl); | ||
| 33 | } | ||
| 34 | |||
| 35 | // Add font providers | ||
| 36 | 1 | const fonts = getFonts('providerLists', globals.getThemeSettings(), globals.handlebars); | |
| 37 | 1 | for (const provider in fonts) { | |
| 38 | if ( typeof fontResources[provider] !== 'undefined' ) { |
||
| 39 | 1 | hosts = hosts.concat(fontResources[provider]); | |
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | 1 | hosts = hosts.map(host => { | |
| 44 | try { | ||
| 45 | 2 | return addResourceHint( | |
| 46 | globals, | ||
| 47 | host, | ||
| 48 | resourceHintAllowedStates.preconnectResourceHintState, | ||
| 49 | resourceHintAllowedTypes.resourceHintFontType, | ||
| 50 | resourceHintAllowedCors.anonymousCors | ||
| 51 | ); | ||
| 52 | } catch (e) { | ||
| 53 | console.info(`EarlyHint generation failed in resourceHints helper with host [${host}]`); | ||
| 54 | return host; | ||
| 55 | } | ||
| 56 | }); | ||
| 57 | |||
| 58 | 1 | return new globals.handlebars.SafeString(hosts.map(host => format(host)).join('')); | |
| 59 | }; | ||
| 60 | }; | ||
| 61 | |||
| 62 | 1 | module.exports = [{ | |
| 63 | name: 'resourceHints', | ||
| 64 | factory: factory, | ||
| 65 | }]; | ||
| 66 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const common = require('./lib/common.js'); | |
| 4 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 5 | |||
| 6 | 1 | const factory = globals => { | |
| 7 | return function (string, key, value) { | ||
| 8 | string = common.unwrapIfSafeString(globals.handlebars, string); | ||
| 9 | 18 | key = common.unwrapIfSafeString(globals.handlebars, key); | |
| 10 | 18 | value = common.unwrapIfSafeString(globals.handlebars, value); | |
| 11 | 18 | if (!utils.isString(string) || !common.isValidURL(string)) { | |
| 12 | 6 | throw new ValidationError("Invalid URL passed to setURLQueryParam"); | |
| 13 | 12 | } else if (!utils.isString(key)) { | |
| 14 | 1 | throw new ValidationError("Invalid query parameter key passed to setURLQueryParam"); | |
| 15 | 11 | } else if (!utils.isString(value)) { | |
| 16 | 1 | throw new ValidationError("Invalid query parameter value passed to setURLQueryParam"); | |
| 17 | } | ||
| 18 | |||
| 19 | 10 | const url = new URL(string); | |
| 20 | |||
| 21 | 10 | url.searchParams.set(encodeURIComponent(key), encodeURIComponent(value)); | |
| 22 | |||
| 23 | 10 | return new globals.handlebars.SafeString(url.toString()); | |
| 24 | }; | ||
| 25 | }; | ||
| 26 | |||
| 27 | 1 | module.exports = [{ | |
| 28 | name: 'setURLQueryParam', | ||
| 29 | factory: factory, | ||
| 30 | }]; | ||
| 31 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = () => { | |
| 4 | return function(location) { | ||
| 5 | return '<!-- snippet location ' + location + ' -->'; | ||
| 6 | }; | ||
| 7 | }; | ||
| 8 | |||
| 9 | 1 | module.exports = [{ | |
| 10 | name: 'snippet', | ||
| 11 | factory: factory, | ||
| 12 | }]; | ||
| 13 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const common = require('./lib/common.js'); | |
| 3 | 1 | const { ValidationError } = require('../lib/errors'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function (str, substr, newSubstr, iteration) { | ||
| 7 | str = common.unwrapIfSafeString(globals.handlebars, str); | ||
| 8 | 12 | substr = common.unwrapIfSafeString(globals.handlebars, substr); | |
| 9 | 12 | newSubstr = common.unwrapIfSafeString(globals.handlebars, newSubstr); | |
| 10 | 12 | iteration = common.unwrapIfSafeString(globals.handlebars, iteration); | |
| 11 | |||
| 12 | 12 | if (typeof str !== 'string') { | |
| 13 | 2 | throw new ValidationError("Invalid query parameter string passed to strReplace"); | |
| 14 | 10 | } else if (typeof substr !== 'string') { | |
| 15 | 1 | throw new ValidationError("Invalid query parameter substring passed to strReplace"); | |
| 16 | 9 | } else if (typeof newSubstr !== 'string') { | |
| 17 | 1 | throw new ValidationError("Invalid query parameter new substring passed to strReplace"); | |
| 18 | } | ||
| 19 | |||
| 20 | 8 | if (typeof iteration !== 'number') { | |
| 21 | 3 | return str.replace(new RegExp(escapeRegex(substr), 'g'), newSubstr); | |
| 22 | } | ||
| 23 | |||
| 24 | 5 | const occurrence = getOccurrences(str, substr); | |
| 25 | |||
| 26 | 5 | if (iteration > 0 && occurrence > 0) { | |
| 27 | 3 | if (iteration >= occurrence) { | |
| 28 | 2 | return str.replace(new RegExp(escapeRegex(substr), 'g'), newSubstr); | |
| 29 | } else { | ||
| 30 | 1 | let result = str; | |
| 31 | 1 | for (let i = 0; i < iteration; i++) { | |
| 32 | 2 | result = result.replace(substr, newSubstr); | |
| 33 | } | ||
| 34 | 1 | return result; | |
| 35 | } | ||
| 36 | } else { | ||
| 37 | 2 | return str; | |
| 38 | } | ||
| 39 | }; | ||
| 40 | }; | ||
| 41 | |||
| 42 | function escapeRegex(string) { | ||
| 43 | 10 | return string.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); | |
| 44 | } | ||
| 45 | |||
| 46 | function getOccurrences(str, substr) { | ||
| 47 | 5 | const matches = str.match(new RegExp(escapeRegex(substr), 'g')); | |
| 48 | return matches ? matches.length : 0 ; |
||
| 49 | } | ||
| 50 | |||
| 51 | 1 | module.exports = [{ | |
| 52 | name: 'strReplace', | ||
| 53 | factory: factory, | ||
| 54 | }]; | ||
| 55 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('./3p/utils'); | |
| 3 | 1 | const common = require('./lib/common.js'); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function (url) { | ||
| 7 | url = common.unwrapIfSafeString(globals.handlebars, url); | ||
| 8 | if ( utils.isString(url) ) { |
||
| 9 | 3 | return url.split('?')[0]; | |
| 10 | } | ||
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | 1 | module.exports = [{ | |
| 15 | name: 'stripQuerystring', | ||
| 16 | factory: factory, | ||
| 17 | }]; | ||
| 18 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const buildCDNHelper = require('./lib/cdnify'); | |
| 4 | 1 | const { | |
| 5 | addResourceHint, | ||
| 6 | resourceHintAllowedTypes, | ||
| 7 | resourceHintAllowedCors, | ||
| 8 | defaultResourceHintState | ||
| 9 | } = require('./lib/resourceHints'); | ||
| 10 | 1 | const utils = require('./3p/utils'); | |
| 11 | |||
| 12 | 1 | const factory = globals => { | |
| 13 | const cdnify = buildCDNHelper(globals); | ||
| 14 | |||
| 15 | 677 | return function (assetPath) { | |
| 16 | const siteSettings = globals.getSiteSettings(); | ||
| 17 | 7 | const configId = siteSettings.theme_config_id; | |
| 18 | |||
| 19 | 7 | const options = arguments[arguments.length - 1]; | |
| 20 | |||
| 21 | // append the configId only if the asset path starts with assets/css/ | ||
| 22 | 7 | const path = configId && assetPath.match(/^\/?assets\/css\//) | |
| 23 | ? assetPath.replace(/\.css$/, `-${configId}.css`) | ||
| 24 | : assetPath; | ||
| 25 | |||
| 26 | 7 | let url = cdnify(path); | |
| 27 | 7 | if (utils.isString(url)) { | |
| 28 | 6 | const cross = options.hash.crossorigin || resourceHintAllowedCors.noCors; | |
| 29 | 6 | try { | |
| 30 | 6 | const hintPath = addResourceHint( | |
| 31 | globals, | ||
| 32 | url, | ||
| 33 | defaultResourceHintState, | ||
| 34 | resourceHintAllowedTypes.resourceHintStyleType, | ||
| 35 | cross | ||
| 36 | ); | ||
| 37 | |||
| 38 | if ( utils.isString(hintPath) ) { |
||
| 39 | 6 | url = hintPath; | |
| 40 | } else { | ||
| 41 | console.info(`Early hint generated and invalid path [${hintPath}]. stylesheet tag won't be using it.`); | ||
| 42 | } | ||
| 43 | } catch (e) { | ||
| 44 | console.info(`Early hint generation failed for path [${url}]`, e); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | 7 | const attrs = Object.assign({rel: 'stylesheet'}, options.hash); | |
| 49 | 7 | const keyValuePairs = []; | |
| 50 | 7 | for (const attrsKey in attrs) { | |
| 51 | 11 | keyValuePairs.push(`${attrsKey}="${attrs[attrsKey]}"`); | |
| 52 | } | ||
| 53 | |||
| 54 | 7 | return `<link data-stencil-stylesheet href="${url}" ${keyValuePairs.join(' ')}>`; | |
| 55 | }; | ||
| 56 | }; | ||
| 57 | |||
| 58 | 1 | module.exports = [{ | |
| 59 | name: 'stylesheet', | ||
| 60 | factory: factory, | ||
| 61 | }]; | ||
| 62 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const whitelist = [ | |
| 4 | { | ||
| 5 | name: 'array', | ||
| 6 | include: [ | ||
| 7 | 'after', | ||
| 8 | 'arrayify', | ||
| 9 | 'before', | ||
| 10 | 'eachIndex', | ||
| 11 | 'filter', | ||
| 12 | 'first', | ||
| 13 | 'forEach', | ||
| 14 | 'inArray', | ||
| 15 | 'isArray', | ||
| 16 | 'last', | ||
| 17 | 'lengthEqual', | ||
| 18 | 'map', | ||
| 19 | 'some', | ||
| 20 | 'sort', | ||
| 21 | 'sortBy', | ||
| 22 | 'withAfter', | ||
| 23 | 'withBefore', | ||
| 24 | 'withFirst', | ||
| 25 | 'withLast', | ||
| 26 | 'withSort', | ||
| 27 | ], | ||
| 28 | }, | ||
| 29 | { | ||
| 30 | name: 'collection', | ||
| 31 | include: ['isEmpty', 'iterate', 'length'], | ||
| 32 | }, | ||
| 33 | { | ||
| 34 | name: 'comparison', | ||
| 35 | module: require('./3p/comparison'), | ||
| 36 | include: [ | ||
| 37 | 'and', | ||
| 38 | 'gt', | ||
| 39 | 'gte', | ||
| 40 | 'has', | ||
| 41 | 'eq', | ||
| 42 | 'ifEven', | ||
| 43 | 'ifNth', | ||
| 44 | 'ifOdd', | ||
| 45 | 'is', | ||
| 46 | 'isnt', | ||
| 47 | 'lt', | ||
| 48 | 'lte', | ||
| 49 | 'neither', | ||
| 50 | 'unlessEq', | ||
| 51 | 'unlessGt', | ||
| 52 | 'unlessLt', | ||
| 53 | 'unlessGteq', | ||
| 54 | 'unlessLteq', | ||
| 55 | ], | ||
| 56 | }, | ||
| 57 | { | ||
| 58 | name: 'html', | ||
| 59 | include: ['ellipsis', 'sanitize', 'ul', 'ol', 'thumbnailImage'] | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | name: 'inflection', | ||
| 63 | include: ['inflect', 'ordinalize'], | ||
| 64 | }, | ||
| 65 | { | ||
| 66 | name: 'markdown', | ||
| 67 | include: ['markdown'], | ||
| 68 | }, | ||
| 69 | { | ||
| 70 | name: 'math', | ||
| 71 | include: ['add', 'subtract', 'divide', 'multiply', 'floor', 'ceil', 'round', 'sum', 'avg'], | ||
| 72 | }, | ||
| 73 | { | ||
| 74 | name: 'misc', | ||
| 75 | include: ['default', 'noop', 'withHash'], | ||
| 76 | }, | ||
| 77 | { | ||
| 78 | name: 'number', | ||
| 79 | include: [ | ||
| 80 | 'addCommas', | ||
| 81 | 'phoneNumber', | ||
| 82 | 'random', | ||
| 83 | 'toAbbr', | ||
| 84 | 'toExponential', | ||
| 85 | 'toFixed', | ||
| 86 | 'toFloat', | ||
| 87 | 'toInt', | ||
| 88 | 'toPrecision', | ||
| 89 | ], | ||
| 90 | }, | ||
| 91 | { | ||
| 92 | name: 'object', | ||
| 93 | include: [ | ||
| 94 | 'extend', | ||
| 95 | 'forIn', | ||
| 96 | 'forOwn', | ||
| 97 | 'toPath', | ||
| 98 | 'hasOwn', | ||
| 99 | 'isObject', | ||
| 100 | 'merge', | ||
| 101 | 'JSONparse', | ||
| 102 | 'JSONstringify', | ||
| 103 | ], | ||
| 104 | }, | ||
| 105 | { | ||
| 106 | name: 'string', | ||
| 107 | module: require('./3p/string'), | ||
| 108 | include: [ | ||
| 109 | 'camelcase', | ||
| 110 | 'capitalize', | ||
| 111 | 'capitalizeAll', | ||
| 112 | 'center', | ||
| 113 | 'chop', | ||
| 114 | 'dashcase', | ||
| 115 | 'dotcase', | ||
| 116 | 'hyphenate', | ||
| 117 | 'isString', | ||
| 118 | 'lowercase', | ||
| 119 | 'pascalcase', | ||
| 120 | 'pathcase', | ||
| 121 | 'plusify', | ||
| 122 | 'reverse', | ||
| 123 | 'sentence', | ||
| 124 | 'snakecase', | ||
| 125 | 'split', | ||
| 126 | 'startsWith', | ||
| 127 | 'titleize', | ||
| 128 | 'trim', | ||
| 129 | 'uppercase' | ||
| 130 | ], | ||
| 131 | }, | ||
| 132 | { | ||
| 133 | name: 'url', | ||
| 134 | include: ['encodeURI', 'decodeURI', 'urlResolve', 'urlParse', 'stripProtocol'], | ||
| 135 | }, | ||
| 136 | ]; | ||
| 137 | |||
| 138 | |||
| 139 | // Construct the data structure the caller expects: an array of { name, factory } | ||
| 140 | 1 | const exportedHelpers = []; | |
| 141 | 1 | for (let i = 0; i < whitelist.length; i++) { | |
| 142 | 12 | const spec = whitelist[i]; | |
| 143 | |||
| 144 | // Initialize module | ||
| 145 | 12 | const module = require(`./3p/${spec.name}`); | |
| 146 | if ( typeof spec.init === 'function' ) { |
||
| 147 | spec.init(module); | ||
| 148 | } | ||
| 149 | |||
| 150 | // Pluck whitelisted functions from each helper module and wrap in object of expected format | ||
| 151 | 12 | const moduleWhitelist = spec.include; | |
| 152 | 12 | for (let i = 0; i < moduleWhitelist.length; i++) { | |
| 153 | 105 | const name = moduleWhitelist[i]; | |
| 154 | 105 | exportedHelpers.push({ | |
| 155 | name: name, | ||
| 156 | factory: () => module[name], | ||
| 157 | }); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | 1 | module.exports = exportedHelpers; | |
| 162 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = () => { | |
| 4 | return function(...args) { | ||
| 5 | args.pop(); | ||
| 6 | 6 | const string = args[0]; | |
| 7 | 6 | if (typeof string !== 'string') { | |
| 8 | 4 | return string; | |
| 9 | } | ||
| 10 | |||
| 11 | 2 | return string.toLowerCase(); | |
| 12 | }; | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | module.exports = [{ | |
| 16 | name: 'toLowerCase', | ||
| 17 | factory: factory, | ||
| 18 | }]; | ||
| 19 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const substring = require('stringz').substring; | |
| 4 | |||
| 5 | /* 2017-02-14 | ||
| 6 | * | ||
| 7 | * FUNCTION | ||
| 8 | * truncate(str, length) | ||
| 9 | * | ||
| 10 | * DESCRIPTION (WHAT) | ||
| 11 | * Returns the first X characters in a string (unless it reaches the end | ||
| 12 | * of the string first, in which case it will return fewer). Returns a | ||
| 13 | * new string that is truncated to the given length. | ||
| 14 | * | ||
| 15 | * USE CASE (WHY) | ||
| 16 | * As a merchant, I want to display a card, at the bottom of | ||
| 17 | * my home page that highlights the most recent blog post. In the card, | ||
| 18 | * I want to display the blog thumbnail, title and the first 40 characters | ||
| 19 | * of the post's body. In order to extract the first 40 characters, I need | ||
| 20 | * a Handlebars helper that works like the javascript substring() function. | ||
| 21 | * | ||
| 22 | * USAGE | ||
| 23 | * | ||
| 24 | * {{lang (truncate 'blog.post.body.' 40) }} | ||
| 25 | */ | ||
| 26 | 1 | const factory = globals => { | |
| 27 | return function(...args) { | ||
| 28 | args.pop(); | ||
| 29 | 8 | const [string, length] = args; | |
| 30 | 8 | if (typeof string !== 'string' || string.length === 0) { | |
| 31 | 3 | return string; | |
| 32 | } | ||
| 33 | |||
| 34 | 5 | const truncatedString = substring(string, 0, length); | |
| 35 | |||
| 36 | 5 | return new globals.handlebars.SafeString(truncatedString); | |
| 37 | }; | ||
| 38 | }; | ||
| 39 | |||
| 40 | 1 | module.exports = [{ | |
| 41 | name: 'truncate', | ||
| 42 | factory: factory, | ||
| 43 | }]; | ||
| 44 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const common = require('./lib/common.js'); | |
| 3 | 1 | const {ValidationError} = require("../lib/errors"); | |
| 4 | |||
| 5 | 1 | const factory = globals => { | |
| 6 | return function (value) { | ||
| 7 | const options = arguments[arguments.length - 1]; | ||
| 8 | if ( options.fn ) { |
||
| 9 | throw new ValidationError("Handlebars helper 'typeof' is not a block function."); | ||
| 10 | } | ||
| 11 | 11 | if (arguments.length-1 !== 1) { | |
| 12 | 1 | throw new ValidationError("Handlebars helper 'typeof' requires exactly one argument. Provided: " + (arguments.length-1)); | |
| 13 | } | ||
| 14 | // Possible future issue due to type conversion: future implementation of flag to disable unwrap may be advisable. | ||
| 15 | // TODO: Add flag to disable unwrap | ||
| 16 | 10 | const toTest = common.unwrapIfSafeString(globals.handlebars, value); | |
| 17 | 10 | return typeof toTest; | |
| 18 | }; | ||
| 19 | }; | ||
| 20 | |||
| 21 | 1 | module.exports = [{ | |
| 22 | name: 'typeof', | ||
| 23 | factory: factory, | ||
| 24 | }]; | ||
| 25 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const factory = globals => { | |
| 4 | return function() { | ||
| 5 | const options = arguments[arguments.length - 1]; | ||
| 6 | 10 | arguments[arguments.length - 1] = Object.assign({}, options, { | |
| 7 | fn: options.inverse || (() => false), | ||
| 8 | inverse: options.fn || (() => true), | ||
| 9 | hash: options.hash | ||
| 10 | }); | ||
| 11 | |||
| 12 | 10 | return globals.handlebars.helpers['if'].apply(this, arguments); | |
| 13 | }; | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | module.exports = [{ | |
| 17 | name: 'unless', | ||
| 18 | factory: factory, | ||
| 19 | }]; | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * Returns all of the items in an array after the specified index. | ||
| 13 | * Opposite of [before](#before). | ||
| 14 | * | ||
| 15 | * Given the array `['a', 'b', 'c']`: | ||
| 16 | * | ||
| 17 | * ```handlebars | ||
| 18 | * {{after array 1}} | ||
| 19 | * //=> '["c"]' | ||
| 20 | * ``` | ||
| 21 | * | ||
| 22 | * @param {Array} `array` Collection | ||
| 23 | * @param {Number} `n` Starting index (number of items to exclude) | ||
| 24 | * @return {Array} Array exluding `n` items. | ||
| 25 | * @api public | ||
| 26 | */ | ||
| 27 | |||
| 28 | 1 | helpers.after = function(array, n) { | |
| 29 | 2 | if (!array || utils.isUndefined(array)) {return '';} | |
| 30 | 3 | return array.slice(n); | |
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Cast the given `value` to an array. | ||
| 35 | * | ||
| 36 | * ```handlebars | ||
| 37 | * {{arrayify "foo"}} | ||
| 38 | * //=> '["foo"]' | ||
| 39 | * ``` | ||
| 40 | * @param {any} `value` | ||
| 41 | * @return {Array} | ||
| 42 | * @api public | ||
| 43 | */ | ||
| 44 | |||
| 45 | 1 | helpers.arrayify = function(...args) { | |
| 46 | args.pop(); // remove handlebars options object | ||
| 47 | 5 | const value = args[0]; | |
| 48 | 5 | return value ? (Array.isArray(value) ? value : [value]) : []; | |
| 49 | }; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Return all of the items in the collection before the specified | ||
| 53 | * count. Opposite of [after](#after). | ||
| 54 | * | ||
| 55 | * Given the array `['a', 'b', 'c']`: | ||
| 56 | * | ||
| 57 | * ```handlebars | ||
| 58 | * {{before array 2}} | ||
| 59 | * //=> '["a", "b"]' | ||
| 60 | * ``` | ||
| 61 | * | ||
| 62 | * @param {Array} `array` | ||
| 63 | * @param {Number} `n` | ||
| 64 | * @return {Array} Array excluding items after the given number. | ||
| 65 | * @api public | ||
| 66 | */ | ||
| 67 | |||
| 68 | 1 | helpers.before = function(array, n) { | |
| 69 | 1 | if (utils.isUndefined(array)) {return '';} | |
| 70 | 2 | return array.slice(0, -n); | |
| 71 | }; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * ```handlebars | ||
| 75 | * {{#eachIndex collection}} | ||
| 76 | * {{item}} is {{index}} | ||
| 77 | * {{/eachIndex}} | ||
| 78 | * ``` | ||
| 79 | * @param {Array} `array` | ||
| 80 | * @return {String} | ||
| 81 | * @block | ||
| 82 | * @api public | ||
| 83 | */ | ||
| 84 | |||
| 85 | 1 | helpers.eachIndex = function(array) { | |
| 86 | var result = ''; | ||
| 87 | 2 | if (Array.isArray(array)) { | |
| 88 | 1 | const options = arguments[arguments.length - 1]; | |
| 89 | 1 | for (var i = 0; i < array.length; i++) { | |
| 90 | 8 | result += options.fn({item: array[i], index: i}); | |
| 91 | } | ||
| 92 | } | ||
| 93 | 2 | return result; | |
| 94 | }; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Block helper that filters the given array and renders the block for values that | ||
| 98 | * evaluate to `true`, otherwise the inverse block is returned. | ||
| 99 | * | ||
| 100 | * Given the array `['a', 'b', 'c']`: | ||
| 101 | * | ||
| 102 | * ```handlebars | ||
| 103 | * {{#filter array "foo"}}AAA{{else}}BBB{{/filter}} | ||
| 104 | * //=> 'BBB' | ||
| 105 | * ``` | ||
| 106 | * | ||
| 107 | * @name .filter | ||
| 108 | * @param {Array} `array` | ||
| 109 | * @param {any} `value` | ||
| 110 | * @return {String} | ||
| 111 | * @block | ||
| 112 | * @api public | ||
| 113 | */ | ||
| 114 | |||
| 115 | 1 | helpers.filter = function(array, value) { | |
| 116 | var content = ''; | ||
| 117 | 3 | var results = []; | |
| 118 | 3 | const options = arguments[arguments.length - 1]; | |
| 119 | |||
| 120 | // filter on a specific property | ||
| 121 | var prop = options.hash && options.hash.property; |
||
| 122 | 3 | if (prop) { | |
| 123 | 1 | results = utils.filter(array, function(val) { | |
| 124 | return utils.get(val, prop) === value; | ||
| 125 | }); | ||
| 126 | } else { | ||
| 127 | |||
| 128 | // filter on a string value | ||
| 129 | 2 | results = utils.filter(array, function(v) { | |
| 130 | return value === v; | ||
| 131 | }); | ||
| 132 | } | ||
| 133 | |||
| 134 | if ( results && results.length > 0) { |
||
| 135 | 2 | for (var i = 0; i < results.length; i++) { | |
| 136 | 2 | content += options.fn(results[i]); | |
| 137 | } | ||
| 138 | 2 | return content; | |
| 139 | } | ||
| 140 | 1 | return options.inverse(this); | |
| 141 | }; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Returns the first item, or first `n` items of an array. | ||
| 145 | * | ||
| 146 | * Given the array `['a', 'b', 'c', 'd', 'e']`: | ||
| 147 | * | ||
| 148 | * ```handlebars | ||
| 149 | * {{first array 2}} | ||
| 150 | * //=> '["a", "b"]' | ||
| 151 | * ``` | ||
| 152 | * | ||
| 153 | * @param {Array} `array` | ||
| 154 | * @param {Number} `n` Number of items to return, starting at `0`. | ||
| 155 | * @return {Array} | ||
| 156 | * @api public | ||
| 157 | */ | ||
| 158 | |||
| 159 | 1 | helpers.first = function(array, n) { | |
| 160 | if (Array.isArray(array)) { | ||
| 161 | 9 | return arrayAlt(); | |
| 162 | } | ||
| 163 | |||
| 164 | 9 | if (utils.isString(array)) { | |
| 165 | 5 | const chars = array.split(''); | |
| 166 | 5 | const result = arrayAlt(chars, n); | |
| 167 | return Array.isArray(result) ? result.join('') : result; |
||
| 168 | } | ||
| 169 | |||
| 170 | 4 | return []; | |
| 171 | |||
| 172 | function arrayAlt() { | ||
| 173 | if ( utils.isUndefined(array) ) {return '';} |
||
| 174 | 14 | if (!utils.isNumber(n)) { | |
| 175 | 4 | return array[0]; | |
| 176 | } | ||
| 177 | 10 | return array.slice(0, n); | |
| 178 | } | ||
| 179 | }; | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Iterates over each item in an array and exposes the current item | ||
| 183 | * in the array as context to the inner block. In addition to | ||
| 184 | * the current array item, the helper exposes the following variables | ||
| 185 | * to the inner block: | ||
| 186 | * | ||
| 187 | * - `index` | ||
| 188 | * - `total` | ||
| 189 | * - `isFirst` | ||
| 190 | * - `isLast` | ||
| 191 | * | ||
| 192 | * Also, `@index` is exposed as a private variable, and additional | ||
| 193 | * private variables may be defined as hash arguments. | ||
| 194 | * | ||
| 195 | * ```js | ||
| 196 | * var accounts = [ | ||
| 197 | * {'name': 'John', 'email': 'john@example.com'}, | ||
| 198 | * {'name': 'Malcolm', 'email': 'malcolm@example.com'}, | ||
| 199 | * {'name': 'David', 'email': 'david@example.com'} | ||
| 200 | * ]; | ||
| 201 | * | ||
| 202 | * // example usage | ||
| 203 | * // {{#forEach accounts}} | ||
| 204 | * // <a href="mailto:{{ email }}" title="Send an email to {{ name }}"> | ||
| 205 | * // {{ name }} | ||
| 206 | * // </a>{{#unless isLast}}, {{/unless}} | ||
| 207 | * // {{/forEach}} | ||
| 208 | * ``` | ||
| 209 | * @source <http://stackoverflow.com/questions/13861007> | ||
| 210 | * @param {Array} `array` | ||
| 211 | * @return {String} | ||
| 212 | * @block | ||
| 213 | * @api public | ||
| 214 | */ | ||
| 215 | |||
| 216 | 1 | helpers.forEach = function(array) { | |
| 217 | const options = arguments[arguments.length - 1]; | ||
| 218 | 9 | var data = utils.createFrame(options, options.hash); | |
| 219 | 9 | if (!array) { | |
| 220 | 1 | return ''; | |
| 221 | } | ||
| 222 | 8 | var len = array.length; | |
| 223 | 8 | var buffer = ''; | |
| 224 | 8 | var i = -1; | |
| 225 | |||
| 226 | 8 | while (++i < len) { | |
| 227 | 21 | var item = array[i]; | |
| 228 | 21 | data.index = i; | |
| 229 | 21 | item.index = i + 1; | |
| 230 | 21 | item.total = len; | |
| 231 | 21 | item.isFirst = i === 0; | |
| 232 | 21 | item.isLast = i === (len - 1); | |
| 233 | 21 | buffer += options.fn(item, {data: data}); | |
| 234 | } | ||
| 235 | 8 | return buffer; | |
| 236 | }; | ||
| 237 | |||
| 238 | /** | ||
| 239 | * Block helper that renders the block if an array has the | ||
| 240 | * given `value`. Optionally specify an inverse block to render | ||
| 241 | * when the array does not have the given value. | ||
| 242 | * | ||
| 243 | * Given the array `['a', 'b', 'c']`: | ||
| 244 | * | ||
| 245 | * ```handlebars | ||
| 246 | * {{#inArray array "d"}} | ||
| 247 | * foo | ||
| 248 | * {{else}} | ||
| 249 | * bar | ||
| 250 | * {{/inArray}} | ||
| 251 | * //=> 'bar' | ||
| 252 | * ``` | ||
| 253 | * | ||
| 254 | * @name .inArray | ||
| 255 | * @param {Array} `array` | ||
| 256 | * @param {any} `value` | ||
| 257 | * @return {String} | ||
| 258 | * @block | ||
| 259 | * @api public | ||
| 260 | */ | ||
| 261 | |||
| 262 | 1 | helpers.inArray = function(array, value) { | |
| 263 | const options = arguments[arguments.length - 1]; | ||
| 264 | 2 | if (utils.indexOf(array, value) > -1) { | |
| 265 | 1 | return options.fn(this); | |
| 266 | } | ||
| 267 | 1 | return options.inverse(this); | |
| 268 | }; | ||
| 269 | |||
| 270 | /** | ||
| 271 | * Returns true if `value` is an es5 array. | ||
| 272 | * | ||
| 273 | * ```handlebars | ||
| 274 | * {{isArray "abc"}} | ||
| 275 | * //=> 'false' | ||
| 276 | * ``` | ||
| 277 | * | ||
| 278 | * @param {any} `value` The value to test. | ||
| 279 | * @return {Boolean} | ||
| 280 | * @api public | ||
| 281 | */ | ||
| 282 | |||
| 283 | 1 | helpers.isArray = function(value) { | |
| 284 | return Array.isArray(value); | ||
| 285 | }; | ||
| 286 | |||
| 287 | |||
| 288 | /** | ||
| 289 | * Returns the last item, or last `n` items of an array. | ||
| 290 | * Opposite of [first](#first). | ||
| 291 | * | ||
| 292 | * Given the array `['a', 'b', 'c', 'd', 'e']`: | ||
| 293 | * | ||
| 294 | * ```handlebars | ||
| 295 | * {{last array 2}} | ||
| 296 | * //=> '["d", "e"]' | ||
| 297 | * ``` | ||
| 298 | * @param {Array} `array` | ||
| 299 | * @param {Number} `n` Number of items to return, starting with the last item. | ||
| 300 | * @return {Array} | ||
| 301 | * @api public | ||
| 302 | */ | ||
| 303 | |||
| 304 | 1 | helpers.last = function(array, n) { | |
| 305 | |||
| 306 | function arrayAlt(array, n) { | ||
| 307 | 11 | if (!utils.isNumber(n)) { | |
| 308 | 3 | return array[array.length - 1]; | |
| 309 | } | ||
| 310 | 8 | return array.slice(-n); | |
| 311 | } | ||
| 312 | |||
| 313 | function stringAlt(str, n) { | ||
| 314 | 5 | const chars = str.split(''); | |
| 315 | 5 | const result = arrayAlt(chars, n); | |
| 316 | 5 | return Array.isArray(result) ? result.join('') : result; | |
| 317 | } | ||
| 318 | |||
| 319 | 16 | if (Array.isArray(array)) { | |
| 320 | 6 | return arrayAlt(array, n); | |
| 321 | } | ||
| 322 | |||
| 323 | 10 | if (utils.isString(array)) { | |
| 324 | 5 | return stringAlt(array, n); | |
| 325 | } | ||
| 326 | |||
| 327 | 5 | return []; | |
| 328 | }; | ||
| 329 | |||
| 330 | /** | ||
| 331 | * Block helper that compares the length of the given array to | ||
| 332 | * the number passed as the second argument. If the array length | ||
| 333 | * is equal to the given `length`, the block is returned, | ||
| 334 | * otherwise an inverse block may optionally be returned. | ||
| 335 | * | ||
| 336 | * Given the array `['a', 'b', 'c', 'd', 'e']`: | ||
| 337 | * | ||
| 338 | * ```handlebars | ||
| 339 | * {{#lengthEqual array 10}}AAA{{else}}BBB{{/lengthEqual}} | ||
| 340 | * //=> 'BBB' | ||
| 341 | * ``` | ||
| 342 | * | ||
| 343 | * @name .lengthEqual | ||
| 344 | * @param {Array} `array` | ||
| 345 | * @param {Number} `length` | ||
| 346 | * @return {String} | ||
| 347 | * @block | ||
| 348 | * @api public | ||
| 349 | */ | ||
| 350 | |||
| 351 | 1 | helpers.lengthEqual = function(array, length) { | |
| 352 | const options = arguments[arguments.length - 1]; | ||
| 353 | 2 | if (array.length === length) { | |
| 354 | 1 | return options.fn(this); | |
| 355 | } | ||
| 356 | 1 | return options.inverse(this); | |
| 357 | }; | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Returns a new array, created by calling `function` | ||
| 361 | * on each element of the given `array`. | ||
| 362 | * | ||
| 363 | * Given an array `['a', 'b', 'c']`: | ||
| 364 | * | ||
| 365 | * ```js | ||
| 366 | * // register `double` as a helper | ||
| 367 | * function double(str) { | ||
| 368 | * return str + str; | ||
| 369 | * } | ||
| 370 | * // then used like this: | ||
| 371 | * // {{map array double}} | ||
| 372 | * //=> '["aa", "bb", "cc"]' | ||
| 373 | * ``` | ||
| 374 | * | ||
| 375 | * @param {Array} `array` | ||
| 376 | * @param {Function} `fn` | ||
| 377 | * @return {String} | ||
| 378 | * @api public | ||
| 379 | */ | ||
| 380 | |||
| 381 | 1 | helpers.map = function(array, fn) { | |
| 382 | 1 | if (utils.isUndefined(array)) {return '';} | |
| 383 | 3 | if (typeof array === 'string' && /[[]/.test(array)) { | |
| 384 | 2 | array = utils.tryParse(array) || []; | |
| 385 | } | ||
| 386 | 3 | var len = array.length; | |
| 387 | 3 | var res = new Array(len); | |
| 388 | 3 | var i = -1; | |
| 389 | |||
| 390 | 3 | while (++i < len) { | |
| 391 | 6 | res[i] = fn(array[i], i, array); | |
| 392 | } | ||
| 393 | 3 | return res; | |
| 394 | }; | ||
| 395 | |||
| 396 | /** | ||
| 397 | * Block helper that returns the block if the callback returns true | ||
| 398 | * for some value in the given array. | ||
| 399 | * | ||
| 400 | * Given the array `[1, 'b', 3]`: | ||
| 401 | * | ||
| 402 | * ```handlebars | ||
| 403 | * {{#some array isString}} | ||
| 404 | * Render me if the array has a string. | ||
| 405 | * {{else}} | ||
| 406 | * Render me if it doesn't. | ||
| 407 | * {{/some}} | ||
| 408 | * //=> 'Render me if the array has a string.' | ||
| 409 | * ``` | ||
| 410 | * @name .some | ||
| 411 | * @param {Array} `array` | ||
| 412 | * @param {Function} `cb` callback function | ||
| 413 | * @return {Array} | ||
| 414 | * @block | ||
| 415 | * @api public | ||
| 416 | */ | ||
| 417 | |||
| 418 | 1 | helpers.some = function(arr, cb) { | |
| 419 | const options = arguments[arguments.length - 1]; | ||
| 420 | 3 | cb = utils.iterator(cb, this); | |
| 421 | 3 | if (!arr) { | |
| 422 | 1 | return options.inverse(this); | |
| 423 | } | ||
| 424 | 2 | var len = arr.length, i = -1; | |
| 425 | 2 | while (++i < len) { | |
| 426 | 4 | if (cb(arr[i], i, arr)) { | |
| 427 | 1 | return options.fn(this); | |
| 428 | } | ||
| 429 | } | ||
| 430 | 1 | return options.inverse(this); | |
| 431 | }; | ||
| 432 | |||
| 433 | /** | ||
| 434 | * Sort the given `array`. If an array of objects is passed, | ||
| 435 | * you may optionally pass a `key` to sort on as the second | ||
| 436 | * argument. You may alternatively pass a sorting function as | ||
| 437 | * the second argument. | ||
| 438 | * | ||
| 439 | * Given an array `['b', 'a', 'c']`: | ||
| 440 | * | ||
| 441 | * ```handlebars | ||
| 442 | * {{sort array}} | ||
| 443 | * //=> '["a", "b", "c"]' | ||
| 444 | * ``` | ||
| 445 | * | ||
| 446 | * @param {Array} `array` the array to sort. | ||
| 447 | * @param {String|Function} `key` The object key to sort by, or sorting function. | ||
| 448 | * @api public | ||
| 449 | */ | ||
| 450 | |||
| 451 | 1 | helpers.sort = function(arr, options) { | |
| 452 | 1 | if (utils.isUndefined(arr)) {return '';} | |
| 453 | 3 | if (utils.get(options, 'hash.reverse')) { | |
| 454 | 1 | return arr.sort().reverse(); | |
| 455 | } | ||
| 456 | 2 | return arr.sort(); | |
| 457 | }; | ||
| 458 | |||
| 459 | /** | ||
| 460 | * Sort an `array`. If an array of objects is passed, | ||
| 461 | * you may optionally pass a `key` to sort on as the second | ||
| 462 | * argument. You may alternatively pass a sorting function as | ||
| 463 | * the second argument. | ||
| 464 | * | ||
| 465 | * Given an array `[{a: 'zzz'}, {a: 'aaa'}]`: | ||
| 466 | * | ||
| 467 | * ```handlebars | ||
| 468 | * {{sortBy array "a"}} | ||
| 469 | * //=> '[{"a":"aaa"}, {"a":"zzz"}]' | ||
| 470 | * ``` | ||
| 471 | * | ||
| 472 | * @param {Array} `array` the array to sort. | ||
| 473 | * @param {String|Function} `props` One or more properties to sort by, or sorting functions to use. | ||
| 474 | * @api public | ||
| 475 | */ | ||
| 476 | |||
| 477 | 1 | helpers.sortBy = function(arr/*, prop*/) { | |
| 478 | 1 | if (utils.isUndefined(arr)) {return '';} | |
| 479 | 4 | var args = [].slice.call(arguments); | |
| 480 | 4 | args.pop(); // remove hbs options object | |
| 481 | |||
| 482 | 4 | if (typeof args[0] === 'string' && /[[]/.test(args[0])) { | |
| 483 | 3 | args[0] = utils.tryParse(args[0]) || []; | |
| 484 | } | ||
| 485 | 4 | if (utils.isUndefined(args[1])) { | |
| 486 | 2 | return args[0].sort(); | |
| 487 | } | ||
| 488 | 2 | return utils.sortBy.apply(null, args); | |
| 489 | }; | ||
| 490 | |||
| 491 | /** | ||
| 492 | * Use the items in the array _after_ the specified index | ||
| 493 | * as context inside a block. Opposite of [withBefore](#withBefore). | ||
| 494 | * | ||
| 495 | * Given the array `['a', 'b', 'c', 'd', 'e']`: | ||
| 496 | * | ||
| 497 | * ```handlebars | ||
| 498 | * {{#withAfter array 3}} | ||
| 499 | * {{this}} | ||
| 500 | * {{/withAfter}} | ||
| 501 | * //=> "de" | ||
| 502 | * ``` | ||
| 503 | * @param {Array} `array` | ||
| 504 | * @param {Number} `idx` | ||
| 505 | * @return {Array} | ||
| 506 | * @block | ||
| 507 | * @api public | ||
| 508 | */ | ||
| 509 | |||
| 510 | 1 | helpers.withAfter = function(array, idx) { | |
| 511 | const options = arguments[arguments.length - 1]; | ||
| 512 | 1 | array = array.slice(idx); | |
| 513 | 1 | var result = ''; | |
| 514 | |||
| 515 | 1 | var len = array.length, i = -1; | |
| 516 | 1 | while (++i < len) { | |
| 517 | 3 | result += options.fn(array[i]); | |
| 518 | } | ||
| 519 | 1 | return result; | |
| 520 | }; | ||
| 521 | |||
| 522 | /** | ||
| 523 | * Use the items in the array _before_ the specified index | ||
| 524 | * as context inside a block. Opposite of [withAfter](#withAfter). | ||
| 525 | * | ||
| 526 | * Given the array `['a', 'b', 'c', 'd', 'e']`: | ||
| 527 | * | ||
| 528 | * ```handlebars | ||
| 529 | * {{#withBefore array 3}} | ||
| 530 | * {{this}} | ||
| 531 | * {{/withBefore}} | ||
| 532 | * //=> 'ab' | ||
| 533 | * ``` | ||
| 534 | * @param {Array} `array` | ||
| 535 | * @param {Number} `idx` | ||
| 536 | * @return {Array} | ||
| 537 | * @block | ||
| 538 | * @api public | ||
| 539 | */ | ||
| 540 | |||
| 541 | 1 | helpers.withBefore = function(array, idx) { | |
| 542 | const options = arguments[arguments.length - 1]; | ||
| 543 | 1 | array = array.slice(0, -idx); | |
| 544 | 1 | var result = ''; | |
| 545 | |||
| 546 | 1 | var len = array.length, i = -1; | |
| 547 | 1 | while (++i < len) { | |
| 548 | 3 | result += options.fn(array[i]); | |
| 549 | } | ||
| 550 | 1 | return result; | |
| 551 | }; | ||
| 552 | |||
| 553 | /** | ||
| 554 | * Use the first item in a collection inside a handlebars | ||
| 555 | * block expression. Opposite of [withLast](#withLast). | ||
| 556 | * | ||
| 557 | * Given the array `['a', 'b', 'c']`: | ||
| 558 | * | ||
| 559 | * ```handlebars | ||
| 560 | * {{#withFirst array}} | ||
| 561 | * {{this}} | ||
| 562 | * {{/withFirst}} | ||
| 563 | * //=> 'a' | ||
| 564 | * ``` | ||
| 565 | * @param {Array} `array` | ||
| 566 | * @param {Number} `idx` | ||
| 567 | * @return {String} | ||
| 568 | * @block | ||
| 569 | * @api public | ||
| 570 | */ | ||
| 571 | |||
| 572 | 1 | helpers.withFirst = function(arr, idx) { | |
| 573 | const options = arguments[arguments.length - 1]; | ||
| 574 | 4 | if (utils.isEmpty(arr) || utils.isUndefined(arr)) { | |
| 575 | 2 | return ''; | |
| 576 | } | ||
| 577 | 2 | arr = utils.result(arr); | |
| 578 | |||
| 579 | 2 | if (!utils.isUndefined(idx)) { | |
| 580 | 1 | idx = parseFloat(utils.result(idx)); | |
| 581 | } | ||
| 582 | |||
| 583 | 2 | if (utils.isUndefined(idx)) { | |
| 584 | 1 | return options.fn(arr[0]); | |
| 585 | } | ||
| 586 | |||
| 587 | 1 | arr = arr.slice(0, idx); | |
| 588 | 1 | var len = arr.length, i = -1; | |
| 589 | 1 | var result = ''; | |
| 590 | 1 | while (++i < len) { | |
| 591 | 2 | result += options.fn(arr[i]); | |
| 592 | } | ||
| 593 | 1 | return result; | |
| 594 | }; | ||
| 595 | |||
| 596 | /** | ||
| 597 | * Use the last item or `n` items in an array as context inside a block. | ||
| 598 | * Opposite of [withFirst](#withFirst). | ||
| 599 | * | ||
| 600 | * Given the array `['a', 'b', 'c']`: | ||
| 601 | * | ||
| 602 | * ```handlebars | ||
| 603 | * {{#withLast array}} | ||
| 604 | * {{this}} | ||
| 605 | * {{/withLast}} | ||
| 606 | * //=> 'c' | ||
| 607 | * ``` | ||
| 608 | * @param {Array} `array` | ||
| 609 | * @param {Number} `idx` The starting index. | ||
| 610 | * @return {String} | ||
| 611 | * @block | ||
| 612 | * @api public | ||
| 613 | */ | ||
| 614 | |||
| 615 | 1 | helpers.withLast = function(array, idx) { | |
| 616 | 1 | if (utils.isUndefined(array)) {return '';} | |
| 617 | 2 | const options = arguments[arguments.length - 1]; | |
| 618 | 2 | array = utils.result(array); | |
| 619 | |||
| 620 | 2 | if (!utils.isUndefined(idx)) { | |
| 621 | 1 | idx = parseFloat(utils.result(idx)); | |
| 622 | } | ||
| 623 | |||
| 624 | 2 | if (utils.isUndefined(idx)) { | |
| 625 | 1 | return options.fn(array[array.length - 1]); | |
| 626 | } | ||
| 627 | |||
| 628 | 1 | array = array.slice(-idx); | |
| 629 | 1 | var len = array.length, i = -1; | |
| 630 | 1 | var result = ''; | |
| 631 | 1 | while (++i < len) { | |
| 632 | 2 | result += options.fn(array[i]); | |
| 633 | } | ||
| 634 | 1 | return result; | |
| 635 | }; | ||
| 636 | |||
| 637 | /** | ||
| 638 | * Block helper that sorts a collection and exposes the sorted | ||
| 639 | * collection as context inside the block. | ||
| 640 | * | ||
| 641 | * Given the array `['b', 'a', 'c']`: | ||
| 642 | * | ||
| 643 | * ```handlebars | ||
| 644 | * {{#withSort array}}{{this}}{{/withSort}} | ||
| 645 | * //=> 'abc' | ||
| 646 | * ``` | ||
| 647 | * @name .withSort | ||
| 648 | * @param {Array} `array` | ||
| 649 | * @param {String} `prop` | ||
| 650 | * @return {String} | ||
| 651 | * @block | ||
| 652 | * @api public | ||
| 653 | */ | ||
| 654 | |||
| 655 | 1 | helpers.withSort = function(array, prop) { | |
| 656 | 1 | if (utils.isUndefined(array)) {return '';} | |
| 657 | 4 | const options = arguments[arguments.length - 1]; | |
| 658 | 4 | var result = ''; | |
| 659 | |||
| 660 | 4 | if (utils.isUndefined(prop)) { | |
| 661 | 2 | array = array.sort(); | |
| 662 | 2 | if (utils.get(options, 'hash.reverse')) { | |
| 663 | 1 | array = array.reverse(); | |
| 664 | } | ||
| 665 | |||
| 666 | 2 | for (var i = 0, len = array.length; i < len; i++) { | |
| 667 | 16 | result += options.fn(array[i]); | |
| 668 | } | ||
| 669 | 2 | return result; | |
| 670 | } | ||
| 671 | |||
| 672 | 2 | array.sort(function(a, b) { | |
| 673 | a = utils.get(a, prop); | ||
| 674 | 4 | b = utils.get(b, prop); | |
| 675 | return a > b ? 1 : ( a < b ? -1 : 0 ); |
||
| 676 | }); | ||
| 677 | |||
| 678 | 2 | if (utils.get(options, 'hash.reverse')) { | |
| 679 | 1 | array = array.reverse(); | |
| 680 | } | ||
| 681 | |||
| 682 | 2 | var alen = array.length, j = -1; | |
| 683 | 2 | while (++j < alen) { | |
| 684 | 6 | result += options.fn(array[j]); | |
| 685 | } | ||
| 686 | 2 | return result; | |
| 687 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var array = require('./array'); | |
| 4 | 1 | var object = require('./object'); | |
| 5 | 1 | var utils = require('./utils'); | |
| 6 | 1 | var forEach = array.forEach; | |
| 7 | 1 | var forOwn = object.forOwn; | |
| 8 | |||
| 9 | /** | ||
| 10 | * Expose `helpers` | ||
| 11 | */ | ||
| 12 | |||
| 13 | 1 | var helpers = module.exports; | |
| 14 | |||
| 15 | /** | ||
| 16 | * Block helper that returns a block if the given collection is | ||
| 17 | * empty. If the collection is not empty the inverse block is returned | ||
| 18 | * (if supplied). | ||
| 19 | * | ||
| 20 | * @name .isEmpty | ||
| 21 | * @param {Object} `collection` | ||
| 22 | * @return {String} | ||
| 23 | * @block | ||
| 24 | * @api public | ||
| 25 | */ | ||
| 26 | |||
| 27 | 1 | helpers.isEmpty = function(collection) { | |
| 28 | const options = arguments[arguments.length - 1]; | ||
| 29 | 5 | if (utils.isOptions(collection)) { | |
| 30 | 1 | return collection.fn(this); | |
| 31 | } | ||
| 32 | |||
| 33 | 4 | if (Array.isArray(collection) && !collection.length) { | |
| 34 | 1 | return options.fn(this); | |
| 35 | } | ||
| 36 | |||
| 37 | 3 | var keys = Object.keys(collection); | |
| 38 | if ( typeof collection === 'object' && !keys.length) { |
||
| 39 | 1 | return options.fn(this); | |
| 40 | } | ||
| 41 | 2 | return options.inverse(this); | |
| 42 | }; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Iterate over an array or object, | ||
| 46 | * | ||
| 47 | * @name .iterate | ||
| 48 | * @param {Object|Array} `context` The collection to iterate over | ||
| 49 | * @return {String} | ||
| 50 | * @block | ||
| 51 | * @api public | ||
| 52 | */ | ||
| 53 | |||
| 54 | 1 | helpers.iterate = function(context) { | |
| 55 | const options = arguments[arguments.length - 1]; | ||
| 56 | 5 | if (Array.isArray(context)) { | |
| 57 | 2 | return forEach.apply(forEach, arguments); | |
| 58 | 3 | } else if (utils.isObject(context)) { | |
| 59 | 2 | return forOwn.apply(forOwn, arguments); | |
| 60 | } | ||
| 61 | 1 | return options.inverse(this); | |
| 62 | }; | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Returns the length of the given collection. When using a string literal in the | ||
| 66 | * template, the string must be value JSON. See the example below. Otherwise pass | ||
| 67 | * in an array or object from the context | ||
| 68 | * | ||
| 69 | * ```handlebars | ||
| 70 | * {{length '["a", "b", "c"]'}} | ||
| 71 | * //=> 3 | ||
| 72 | * | ||
| 73 | * //=> myArray = ['a', 'b', 'c', 'd', 'e']; | ||
| 74 | * {{length myArray}} | ||
| 75 | * //=> 5 | ||
| 76 | * | ||
| 77 | * //=> myObject = {'a': 'a', 'b': 'b'}; | ||
| 78 | * {{length myObject}} | ||
| 79 | * //=> 2 | ||
| 80 | * ``` | ||
| 81 | * @param {Array|Object|String} `value` | ||
| 82 | * @return {Number} The length of the value. | ||
| 83 | * @api public | ||
| 84 | */ | ||
| 85 | |||
| 86 | 1 | helpers.length = function(value) { | |
| 87 | 2 | if (!value || utils.isUndefined(value)) {return '';} | |
| 88 | 6 | if (typeof value === 'string' && /[[]/.test(value)) { | |
| 89 | 2 | value = utils.tryParse(value) || []; | |
| 90 | } | ||
| 91 | 6 | if (utils.isObject(value)) { | |
| 92 | 1 | value = Object.keys(value); | |
| 93 | } | ||
| 94 | 6 | return value.length; | |
| 95 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var isObject = require('./object').isObject; | |
| 4 | 1 | var isString = require('./string').isString; | |
| 5 | 1 | var utils = require('./utils'); | |
| 6 | |||
| 7 | /** | ||
| 8 | * Expose `helpers` | ||
| 9 | */ | ||
| 10 | |||
| 11 | 1 | var helpers = module.exports; | |
| 12 | |||
| 13 | /** | ||
| 14 | * Block helper that renders the block if **both** of the given values | ||
| 15 | * are truthy. If an inverse block is specified it will be rendered | ||
| 16 | * when falsy. | ||
| 17 | * | ||
| 18 | * @param {any} `a` | ||
| 19 | * @param {any} `b` | ||
| 20 | * @return {String} | ||
| 21 | * @block | ||
| 22 | * @api public | ||
| 23 | */ | ||
| 24 | |||
| 25 | 1 | helpers.and = function(a, b) { | |
| 26 | const options = arguments[arguments.length - 1]; | ||
| 27 | if ( a && b) { |
||
| 28 | 1 | return options.fn(this); | |
| 29 | } | ||
| 30 | 1 | return options.inverse(this); | |
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Block helper that renders a block if `a` is **greater than** `b`. | ||
| 35 | * | ||
| 36 | * If an inverse block is specified it will be rendered when falsy. | ||
| 37 | * You may optionally use the `compare=""` hash argument for the | ||
| 38 | * second value. | ||
| 39 | * | ||
| 40 | * @name .gt | ||
| 41 | * @param {String} `a` | ||
| 42 | * @param {String} `b` | ||
| 43 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 44 | * @block | ||
| 45 | * @api public | ||
| 46 | */ | ||
| 47 | |||
| 48 | 1 | helpers.gt = function(a, b) { | |
| 49 | const options = arguments[arguments.length - 1]; | ||
| 50 | 6 | if (arguments.length === 2) { | |
| 51 | 3 | b = options.hash.compare; | |
| 52 | } | ||
| 53 | 6 | if (a > b) { | |
| 54 | 2 | return options.fn(this); | |
| 55 | } | ||
| 56 | 4 | return options.inverse(this); | |
| 57 | }; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Block helper that renders a block if `a` is **greater than or | ||
| 61 | * equal to** `b`. | ||
| 62 | * | ||
| 63 | * If an inverse block is specified it will be rendered when falsy. | ||
| 64 | * You may optionally use the `compare=""` hash argument for the | ||
| 65 | * second value. | ||
| 66 | * | ||
| 67 | * @name .gte | ||
| 68 | * @param {String} `a` | ||
| 69 | * @param {String} `b` | ||
| 70 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 71 | * @block | ||
| 72 | * @api public | ||
| 73 | */ | ||
| 74 | |||
| 75 | 1 | helpers.gte = function(a, b) { | |
| 76 | const options = arguments[arguments.length - 1]; | ||
| 77 | 6 | if (arguments.length === 2) { | |
| 78 | 3 | b = options.hash.compare; | |
| 79 | } | ||
| 80 | 6 | if (a >= b) { | |
| 81 | 4 | return options.fn(this); | |
| 82 | } | ||
| 83 | 2 | return options.inverse(this); | |
| 84 | }; | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Block helper that renders a block if `value` has `pattern`. | ||
| 88 | * If an inverse block is specified it will be rendered when falsy. | ||
| 89 | * | ||
| 90 | * @param {any} `val` The value to check. | ||
| 91 | * @param {any} `pattern` The pattern to check for. | ||
| 92 | * @return {String} | ||
| 93 | * @block | ||
| 94 | * @api public | ||
| 95 | */ | ||
| 96 | |||
| 97 | 1 | helpers.has = function(value, pattern) { | |
| 98 | const options = arguments[arguments.length - 1]; | ||
| 99 | 8 | if (arguments.length === 2) { | |
| 100 | 1 | return pattern.inverse(this); | |
| 101 | } | ||
| 102 | |||
| 103 | 7 | if (arguments.length === 1) { | |
| 104 | 1 | return value.inverse(this); | |
| 105 | } | ||
| 106 | |||
| 107 | 6 | if ((Array.isArray(value) || isString(value)) && isString(pattern)) { | |
| 108 | 5 | if (value.indexOf(pattern) > -1) { | |
| 109 | 3 | return options.fn(this); | |
| 110 | } | ||
| 111 | } | ||
| 112 | if (isObject(value) && isString(pattern) && pattern in value) { |
||
| 113 | 1 | return options.fn(this); | |
| 114 | } | ||
| 115 | 2 | return options.inverse(this); | |
| 116 | }; | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Block helper that renders a block if `a` is **equal to** `b`. | ||
| 120 | * If an inverse block is specified it will be rendered when falsy. | ||
| 121 | * You may optionally use the `compare=""` hash argument for the | ||
| 122 | * second value. | ||
| 123 | * | ||
| 124 | * @name .eq | ||
| 125 | * @param {String} `a` | ||
| 126 | * @param {String} `b` | ||
| 127 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 128 | * @block | ||
| 129 | * @api public | ||
| 130 | */ | ||
| 131 | |||
| 132 | 1 | helpers.eq = function(a, b) { | |
| 133 | const options = arguments[arguments.length - 1]; | ||
| 134 | 3 | if (arguments.length === 2) { | |
| 135 | 2 | b = options.hash.compare; | |
| 136 | } | ||
| 137 | 3 | if (a === b) { | |
| 138 | 1 | return options.fn(this); | |
| 139 | } | ||
| 140 | 2 | return options.inverse(this); | |
| 141 | }; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Return true if the given value is an even number. | ||
| 145 | * | ||
| 146 | * ```handlebars | ||
| 147 | * {{#ifEven value}} | ||
| 148 | * render A | ||
| 149 | * {{else}} | ||
| 150 | * render B | ||
| 151 | * {{/ifEven}} | ||
| 152 | * ``` | ||
| 153 | * @param {Number} `number` | ||
| 154 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 155 | * @block | ||
| 156 | * @api public | ||
| 157 | */ | ||
| 158 | |||
| 159 | 1 | helpers.ifEven = function(num) { | |
| 160 | const options = arguments[arguments.length - 1]; | ||
| 161 | 2 | return utils.isEven(num) | |
| 162 | ? options.fn(this) | ||
| 163 | : options.inverse(this); | ||
| 164 | }; | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Conditionally renders a block if the remainder is zero when | ||
| 168 | * `a` operand is divided by `b`. If an inverse block is specified | ||
| 169 | * it will be rendered when the remainder is **not zero**. | ||
| 170 | * | ||
| 171 | * @param {Number} | ||
| 172 | * @param {Number} | ||
| 173 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 174 | * @block | ||
| 175 | * @api public | ||
| 176 | */ | ||
| 177 | |||
| 178 | 1 | helpers.ifNth = function(a, b) { | |
| 179 | const options = arguments[arguments.length - 1]; | ||
| 180 | if ( utils.isNumber(a) && utils.isNumber(b) && b % a === 0) { |
||
| 181 | 3 | return options.fn(this); | |
| 182 | } | ||
| 183 | 2 | return options.inverse(this); | |
| 184 | }; | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Block helper that renders a block if `value` is **an odd number**. | ||
| 188 | * If an inverse block is specified it will be rendered when falsy. | ||
| 189 | * | ||
| 190 | * ```handlebars | ||
| 191 | * {{#ifOdd value}} | ||
| 192 | * render A | ||
| 193 | * {{else}} | ||
| 194 | * render B | ||
| 195 | * {{/ifOdd}} | ||
| 196 | * ``` | ||
| 197 | * @param {Object} `value` | ||
| 198 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 199 | * @block | ||
| 200 | * @api public | ||
| 201 | */ | ||
| 202 | |||
| 203 | 1 | helpers.ifOdd = function(val) { | |
| 204 | const options = arguments[arguments.length - 1]; | ||
| 205 | 2 | return utils.isOdd(val) | |
| 206 | ? options.fn(this) | ||
| 207 | : options.inverse(this); | ||
| 208 | }; | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Block helper that renders a block if `a` is **equal to** `b`. | ||
| 212 | * If an inverse block is specified it will be rendered when falsy. | ||
| 213 | * | ||
| 214 | * @name .is | ||
| 215 | * @param {any} `a` | ||
| 216 | * @param {any} `b` | ||
| 217 | * @return {String} | ||
| 218 | * @block | ||
| 219 | * @api public | ||
| 220 | */ | ||
| 221 | |||
| 222 | 1 | helpers.is = function(a, b) { | |
| 223 | const options = arguments[arguments.length - 1]; | ||
| 224 | 3 | if (arguments.length === 2) { | |
| 225 | 1 | b = options.hash.compare; | |
| 226 | } | ||
| 227 | 3 | if (a === b) { | |
| 228 | 2 | return options.fn(this); | |
| 229 | } | ||
| 230 | 1 | return options.inverse(this); | |
| 231 | }; | ||
| 232 | |||
| 233 | /** | ||
| 234 | * Block helper that renders a block if `a` is **not equal to** `b`. | ||
| 235 | * If an inverse block is specified it will be rendered when falsy. | ||
| 236 | * | ||
| 237 | * @name .isnt | ||
| 238 | * @param {String} `a` | ||
| 239 | * @param {String} `b` | ||
| 240 | * @return {String} | ||
| 241 | * @block | ||
| 242 | * @api public | ||
| 243 | */ | ||
| 244 | |||
| 245 | 1 | helpers.isnt = function(a, b) { | |
| 246 | const options = arguments[arguments.length - 1]; | ||
| 247 | 3 | if (arguments.length === 2) { | |
| 248 | 1 | b = options.hash.compare; | |
| 249 | } | ||
| 250 | 3 | if (a !== b) { | |
| 251 | 2 | return options.fn(this); | |
| 252 | } | ||
| 253 | 1 | return options.inverse(this); | |
| 254 | }; | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Block helper that renders a block if `a` is **less than** `b`. | ||
| 258 | * | ||
| 259 | * If an inverse block is specified it will be rendered when falsy. | ||
| 260 | * You may optionally use the `compare=""` hash argument for the | ||
| 261 | * second value. | ||
| 262 | * | ||
| 263 | * @name .lt | ||
| 264 | * @param {Object} `context` | ||
| 265 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 266 | * @block | ||
| 267 | * @api public | ||
| 268 | */ | ||
| 269 | |||
| 270 | 1 | helpers.lt = function(a, b) { | |
| 271 | const options = arguments[arguments.length - 1]; | ||
| 272 | 5 | if (arguments.length === 2) { | |
| 273 | 2 | b = options.hash.compare; | |
| 274 | } | ||
| 275 | 5 | if (a < b) { | |
| 276 | 2 | return options.fn(this); | |
| 277 | } | ||
| 278 | 3 | return options.inverse(this); | |
| 279 | }; | ||
| 280 | |||
| 281 | /** | ||
| 282 | * Block helper that renders a block if `a` is **less than or | ||
| 283 | * equal to** `b`. | ||
| 284 | * | ||
| 285 | * If an inverse block is specified it will be rendered when falsy. | ||
| 286 | * You may optionally use the `compare=""` hash argument for the | ||
| 287 | * second value. | ||
| 288 | * | ||
| 289 | * @name .lte | ||
| 290 | * @param {Sring} `a` | ||
| 291 | * @param {Sring} `b` | ||
| 292 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 293 | * @block | ||
| 294 | * @api public | ||
| 295 | */ | ||
| 296 | |||
| 297 | 1 | helpers.lte = function(a, b) { | |
| 298 | const options = arguments[arguments.length - 1]; | ||
| 299 | 6 | if (arguments.length === 2) { | |
| 300 | 3 | b = options.hash.compare; | |
| 301 | } | ||
| 302 | 6 | if (a <= b) { | |
| 303 | 4 | return options.fn(this); | |
| 304 | } | ||
| 305 | 2 | return options.inverse(this); | |
| 306 | }; | ||
| 307 | |||
| 308 | /** | ||
| 309 | * Block helper that renders a block if **neither of** the given values | ||
| 310 | * are truthy. If an inverse block is specified it will be rendered | ||
| 311 | * when falsy. | ||
| 312 | * | ||
| 313 | * @name .neither | ||
| 314 | * @param {any} `a` | ||
| 315 | * @param {any} `b` | ||
| 316 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 317 | * @block | ||
| 318 | * @api public | ||
| 319 | */ | ||
| 320 | |||
| 321 | 1 | helpers.neither = function(a, b) { | |
| 322 | const options = arguments[arguments.length - 1]; | ||
| 323 | 2 | if (!a && !b) { | |
| 324 | 1 | return options.fn(this); | |
| 325 | } | ||
| 326 | 1 | return options.inverse(this); | |
| 327 | }; | ||
| 328 | |||
| 329 | |||
| 330 | /** | ||
| 331 | * Block helper that always renders the inverse block **unless `a` is | ||
| 332 | * is equal to `b`**. | ||
| 333 | * | ||
| 334 | * @name .unlessEq | ||
| 335 | * @param {String} `a` | ||
| 336 | * @param {String} `b` | ||
| 337 | * @return {String} Inverse block by default, or block if falsey. | ||
| 338 | * @block | ||
| 339 | * @api public | ||
| 340 | */ | ||
| 341 | |||
| 342 | 1 | helpers.unlessEq = function(context) { | |
| 343 | const options = arguments[arguments.length - 1]; | ||
| 344 | 2 | if (context === options.hash.compare) { | |
| 345 | 1 | return options.inverse(this); | |
| 346 | } | ||
| 347 | 1 | return options.fn(this); | |
| 348 | }; | ||
| 349 | |||
| 350 | /** | ||
| 351 | * Block helper that always renders the inverse block **unless `a` is | ||
| 352 | * is greater than `b`**. | ||
| 353 | * | ||
| 354 | * @name .unlessGt | ||
| 355 | * @param {Object} `context` | ||
| 356 | * @return {String} Inverse block by default, or block if falsey. | ||
| 357 | * @block | ||
| 358 | * @api public | ||
| 359 | */ | ||
| 360 | |||
| 361 | 1 | helpers.unlessGt = function(context) { | |
| 362 | const options = arguments[arguments.length - 1]; | ||
| 363 | 2 | if (context > options.hash.compare) { | |
| 364 | 1 | return options.inverse(this); | |
| 365 | } | ||
| 366 | 1 | return options.fn(this); | |
| 367 | }; | ||
| 368 | |||
| 369 | /** | ||
| 370 | * Block helper that always renders the inverse block **unless `a` is | ||
| 371 | * is less than `b`**. | ||
| 372 | * | ||
| 373 | * @name .unlessLt | ||
| 374 | * @param {Object} `context` | ||
| 375 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 376 | * @block | ||
| 377 | * @api public | ||
| 378 | */ | ||
| 379 | |||
| 380 | 1 | helpers.unlessLt = function(context) { | |
| 381 | const options = arguments[arguments.length - 1]; | ||
| 382 | 2 | if (context < options.hash.compare) { | |
| 383 | 1 | return options.inverse(this); | |
| 384 | } | ||
| 385 | 1 | return options.fn(this); | |
| 386 | }; | ||
| 387 | |||
| 388 | /** | ||
| 389 | * Block helper that always renders the inverse block **unless `a` is | ||
| 390 | * is greater than or equal to `b`**. | ||
| 391 | * | ||
| 392 | * @name .unlessGteq | ||
| 393 | * @param {Object} `context` | ||
| 394 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 395 | * @block | ||
| 396 | * @api public | ||
| 397 | */ | ||
| 398 | |||
| 399 | 1 | helpers.unlessGteq = function(context) { | |
| 400 | const options = arguments[arguments.length - 1]; | ||
| 401 | 3 | if (context >= options.hash.compare) { | |
| 402 | 2 | return options.inverse(this); | |
| 403 | } | ||
| 404 | 1 | return options.fn(this); | |
| 405 | }; | ||
| 406 | |||
| 407 | /** | ||
| 408 | * Block helper that always renders the inverse block **unless `a` is | ||
| 409 | * is less than or equal to `b`**. | ||
| 410 | * | ||
| 411 | * @name .unlessLteq | ||
| 412 | * @param {Object} `context` | ||
| 413 | * @return {String} Block, or inverse block if specified and falsey. | ||
| 414 | * @block | ||
| 415 | * @api public | ||
| 416 | */ | ||
| 417 | |||
| 418 | 1 | helpers.unlessLteq = function(context) { | |
| 419 | const options = arguments[arguments.length - 1]; | ||
| 420 | 3 | if (context <= options.hash.compare) { | |
| 421 | 2 | return options.inverse(this); | |
| 422 | } | ||
| 423 | 1 | return options.fn(this); | |
| 424 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const utils = require('./utils'); | |
| 4 | 1 | const striptags = require('./utils/lib/striptags'); | |
| 5 | |||
| 6 | function parseAttributes(hash) { | ||
| 7 | 21 | return Object.keys(hash).map(function (key) { | |
| 8 | return key + '="' + hash[key] + '"'; | ||
| 9 | }).join(' '); | ||
| 10 | }; | ||
| 11 | |||
| 12 | function sanitize(str) { | ||
| 13 | 5 | if (!utils.isString(str)) { return ''; } | |
| 14 | 3 | return striptags(str).trim(); | |
| 15 | }; | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Truncate a string by removing all HTML tags and limiting the result | ||
| 19 | * to the specified `length`. Aslo see [ellipsis](#ellipsis). | ||
| 20 | * | ||
| 21 | * ```js | ||
| 22 | * truncate("<span>foo bar baz</span>", 7); | ||
| 23 | * //=> 'foo bar' | ||
| 24 | * ``` | ||
| 25 | * | ||
| 26 | * @name .truncate | ||
| 27 | * @param {String} `str` | ||
| 28 | * @param {Number} `limit` The desired length of the returned string. | ||
| 29 | * @param {String} `suffix` Optionally supply a string to use as a suffix to | ||
| 30 | * denote when the string has been truncated. | ||
| 31 | * @return {String} The truncated string. | ||
| 32 | * @api public | ||
| 33 | */ | ||
| 34 | function truncate(str, limit, suffix) { | ||
| 35 | if ( str && typeof str === 'string') { |
||
| 36 | var ch = typeof suffix === 'string' ? suffix : ''; |
||
| 37 | if ( str.length > limit ) { |
||
| 38 | 2 | return sanitize(str).slice(0, limit - ch.length) + ch; | |
| 39 | } | ||
| 40 | return str; | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Expose `helpers` | ||
| 46 | */ | ||
| 47 | |||
| 48 | 1 | var helpers = module.exports; | |
| 49 | |||
| 50 | /** | ||
| 51 | * Truncates a string to the specified `length`, and appends | ||
| 52 | * it with an elipsis, `…`. | ||
| 53 | * | ||
| 54 | * ```js | ||
| 55 | * {{ellipsis "<span>foo bar baz</span>", 7}} | ||
| 56 | * //=> 'foo bar…' | ||
| 57 | * ``` | ||
| 58 | * @name .ellipsis | ||
| 59 | * @param {String} `str` | ||
| 60 | * @param {Number} `length` The desired length of the returned string. | ||
| 61 | * @return {String} The truncated string. | ||
| 62 | * @api public | ||
| 63 | */ | ||
| 64 | |||
| 65 | 1 | helpers.ellipsis = function (str, limit) { | |
| 66 | if ( str && typeof str === 'string') { |
||
| 67 | 3 | if (str.length <= limit) { | |
| 68 | 1 | return str; | |
| 69 | } | ||
| 70 | 2 | return truncate(str, limit) + '…'; | |
| 71 | } | ||
| 72 | }; | ||
| 73 | |||
| 74 | |||
| 75 | /** | ||
| 76 | * Strip HTML tags from a string, so that only the text nodes | ||
| 77 | * are preserved. | ||
| 78 | * | ||
| 79 | * ```js | ||
| 80 | * {{sanitize "<span>foo</span>"}} | ||
| 81 | * //=> 'foo' | ||
| 82 | * ``` | ||
| 83 | * | ||
| 84 | * @param {String} `str` The string of HTML to sanitize. | ||
| 85 | * @return {String} | ||
| 86 | * @api public | ||
| 87 | */ | ||
| 88 | |||
| 89 | 1 | helpers.sanitize = function (str) { | |
| 90 | return sanitize(str); | ||
| 91 | }; | ||
| 92 | |||
| 93 | |||
| 94 | /** | ||
| 95 | * Block helper for creating unordered lists (`<ul></ul>`) | ||
| 96 | * | ||
| 97 | * @param {Object} `context` | ||
| 98 | * @return {String} | ||
| 99 | * @block | ||
| 100 | * @api public | ||
| 101 | */ | ||
| 102 | |||
| 103 | 1 | helpers.ul = function (context) { | |
| 104 | const options = arguments[arguments.length - 1]; | ||
| 105 | 1 | return ('<ul ' + (parseAttributes(options.hash)) + '>') + context.map(function (item) { | |
| 106 | if ( typeof item !== 'string' ) { |
||
| 107 | 2 | item = options.fn(item); | |
| 108 | } | ||
| 109 | 2 | return '<li>' + item + '</li>'; | |
| 110 | }).join('\n') + '</ul>'; | ||
| 111 | }; | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Block helper for creating ordered lists (`<ol></ol>`) | ||
| 115 | * | ||
| 116 | * @param {Object} `context` | ||
| 117 | * @return {String} | ||
| 118 | * @block | ||
| 119 | * @api public | ||
| 120 | */ | ||
| 121 | |||
| 122 | 1 | helpers.ol = function (context) { | |
| 123 | const options = arguments[arguments.length - 1]; | ||
| 124 | 1 | return ('<ol ' + (parseAttributes(options.hash)) + '>') + context.map(function (item) { | |
| 125 | if ( typeof item !== 'string' ) { |
||
| 126 | 2 | item = options.fn(item); | |
| 127 | } | ||
| 128 | 2 | return '<li>' + item + '</li>'; | |
| 129 | }).join('\n') + '</ol>'; | ||
| 130 | }; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Returns a `<figure>` with a thumbnail linked to a full picture | ||
| 134 | * | ||
| 135 | * @param {Object} `context` Object with values/attributes to add to the generated elements: | ||
| 136 | * @param {String} `context.alt` | ||
| 137 | * @param {String} `context.src` | ||
| 138 | * @param {Number} `context.width` | ||
| 139 | * @param {Number} `context.height` | ||
| 140 | * @return {String} HTML `<figure>` element with image and optional caption/link. | ||
| 141 | * @contributor: Marie Hogebrandt <https://github.com/Melindrea> | ||
| 142 | * @api public | ||
| 143 | */ | ||
| 144 | |||
| 145 | 1 | helpers.thumbnailImage = function (context) { | |
| 146 | var figure = ''; | ||
| 147 | 7 | var image = ''; | |
| 148 | |||
| 149 | 7 | var link = context.full || false; | |
| 150 | 7 | var imageAttributes = { | |
| 151 | alt: context.alt, | ||
| 152 | src: context.thumbnail, | ||
| 153 | width: context.size.width, | ||
| 154 | height: context.size.height | ||
| 155 | }; | ||
| 156 | |||
| 157 | 7 | var figureAttributes = { id: 'image-' + context.id }; | |
| 158 | 7 | var linkAttributes = { href: link, rel: 'thumbnail' }; | |
| 159 | |||
| 160 | 7 | if (context.classes) { | |
| 161 | 3 | if (context.classes.image) { | |
| 162 | 1 | imageAttributes.class = context.classes.image.join(' '); | |
| 163 | } | ||
| 164 | 3 | if (context.classes.figure) { | |
| 165 | 1 | figureAttributes.class = context.classes.figure.join(' '); | |
| 166 | } | ||
| 167 | 3 | if (context.classes.link) { | |
| 168 | 1 | linkAttributes.class = context.classes.link.join(' '); | |
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | 7 | figure += '<figure ' + parseAttributes(figureAttributes) + '>\n'; | |
| 173 | 7 | image += '<img ' + parseAttributes(imageAttributes) + '>\n'; | |
| 174 | |||
| 175 | 7 | if (link) { | |
| 176 | 5 | figure += '<a ' + parseAttributes(linkAttributes) + '>\n' + image + '</a>\n'; | |
| 177 | } else { | ||
| 178 | 2 | figure += image; | |
| 179 | } | ||
| 180 | |||
| 181 | 7 | if (context.caption) { | |
| 182 | 5 | figure += '<figcaption>' + context.caption + '</figcaption>\n'; | |
| 183 | } | ||
| 184 | |||
| 185 | 7 | figure += '</figure>'; | |
| 186 | 7 | return figure; | |
| 187 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * @name .inflect | ||
| 13 | * @param {Number} `count` | ||
| 14 | * @param {String} `singular` The singular form | ||
| 15 | * @param {String} `plural` The plural form | ||
| 16 | * @param {String} `include` | ||
| 17 | * @return {String} | ||
| 18 | * @api public | ||
| 19 | */ | ||
| 20 | |||
| 21 | 1 | helpers.inflect = function(...args) { | |
| 22 | args.pop(); | ||
| 23 | 2 | const [count, singular, plural, include] = args; | |
| 24 | 2 | var word = (count > 1 || count === 0) ? plural : singular; | |
| 25 | |||
| 26 | 2 | if (utils.isUndefined(include) || include === false) { | |
| 27 | 1 | return word; | |
| 28 | } else { | ||
| 29 | 1 | return String(count) + ' ' + word; | |
| 30 | } | ||
| 31 | }; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Returns an ordinalized number (as a string). | ||
| 35 | * | ||
| 36 | * ```handlebars | ||
| 37 | * {{ordinalize 1}} | ||
| 38 | * //=> '1st' | ||
| 39 | * {{ordinalize 21}} | ||
| 40 | * //=> '21st' | ||
| 41 | * {{ordinalize 29}} | ||
| 42 | * //=> '29th' | ||
| 43 | * {{ordinalize 22}} | ||
| 44 | * //=> '22nd' | ||
| 45 | * ``` | ||
| 46 | * | ||
| 47 | * @param {String} `val` The value to ordinalize. | ||
| 48 | * @return {String} The ordinalized number | ||
| 49 | * @api public | ||
| 50 | */ | ||
| 51 | |||
| 52 | 1 | helpers.ordinalize = function(val) { | |
| 53 | var num = Math.abs(Math.round(val)); | ||
| 54 | 7 | var res = num % 100; | |
| 55 | |||
| 56 | 7 | if (utils.indexOf([11, 12, 13], res) >= 0) { | |
| 57 | 1 | return '' + val + 'th'; | |
| 58 | } | ||
| 59 | |||
| 60 | 6 | switch (num % 10) { | |
| 61 | case 1: | ||
| 62 | 2 | return '' + val + 'st'; | |
| 63 | case 2: | ||
| 64 | 2 | return '' + val + 'nd'; | |
| 65 | case 3: | ||
| 66 | 1 | return '' + val + 'rd'; | |
| 67 | default: { | ||
| 68 | 1 | return '' + val + 'th'; | |
| 69 | } | ||
| 70 | } | ||
| 71 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const markdown = require('./utils/lib/markdown'); | |
| 4 | /** | ||
| 5 | * Expose markdown `helpers` (for performance we're using getters so | ||
| 6 | * that the helpers are only loaded if called) | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * Block helper that converts a string of inline markdown to HTML. | ||
| 13 | * | ||
| 14 | * ```html | ||
| 15 | * {{#markdown}} | ||
| 16 | * # Foo | ||
| 17 | * {{/markdown}} | ||
| 18 | * //=> <h1>Foo</h1> | ||
| 19 | * ``` | ||
| 20 | * @name .markdown | ||
| 21 | * @param {Object} `context` | ||
| 22 | * @param {Object} `options` | ||
| 23 | * @return {String} | ||
| 24 | * @block | ||
| 25 | * @api public | ||
| 26 | */ | ||
| 27 | |||
| 28 | 1 | helpers.markdown = function(context, options) { | |
| 29 | return markdown()(context, options); | ||
| 30 | } | ||
| 31 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * Return the product of `a` plus `b`. | ||
| 13 | * | ||
| 14 | * @param {Number} `a` | ||
| 15 | * @param {Number} `b` | ||
| 16 | * @api public | ||
| 17 | */ | ||
| 18 | |||
| 19 | 1 | helpers.add = function(a, b) { | |
| 20 | return a + b; | ||
| 21 | }; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Return the product of `a` minus `b`. | ||
| 25 | * | ||
| 26 | * @param {Number} `a` | ||
| 27 | * @param {Number} `b` | ||
| 28 | * @api public | ||
| 29 | */ | ||
| 30 | |||
| 31 | 1 | helpers.subtract = function(a, b) { | |
| 32 | return a - b; | ||
| 33 | }; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Divide `a` by `b` | ||
| 37 | * | ||
| 38 | * @param {Number} `a` numerator | ||
| 39 | * @param {Number} `b` denominator | ||
| 40 | * @api public | ||
| 41 | */ | ||
| 42 | |||
| 43 | 1 | helpers.divide = function(a, b) { | |
| 44 | return a / b; | ||
| 45 | }; | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Multiply `a` by `b`. | ||
| 49 | * | ||
| 50 | * @param {Number} `a` factor | ||
| 51 | * @param {Number} `b` multiplier | ||
| 52 | * @api public | ||
| 53 | */ | ||
| 54 | |||
| 55 | 1 | helpers.multiply = function(a, b) { | |
| 56 | return a * b; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Get the `Math.floor()` of the given value. | ||
| 61 | * | ||
| 62 | * @param {Number} `value` | ||
| 63 | * @api public | ||
| 64 | */ | ||
| 65 | |||
| 66 | 1 | helpers.floor = function(value) { | |
| 67 | return Math.floor(value); | ||
| 68 | }; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Get the `Math.ceil()` of the given value. | ||
| 72 | * | ||
| 73 | * @param {Number} `value` | ||
| 74 | * @api public | ||
| 75 | */ | ||
| 76 | |||
| 77 | 1 | helpers.ceil = function(value) { | |
| 78 | return Math.ceil(value); | ||
| 79 | }; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Round the given value. | ||
| 83 | * | ||
| 84 | * @param {Number} `value` | ||
| 85 | * @api public | ||
| 86 | */ | ||
| 87 | |||
| 88 | 1 | helpers.round = function(value) { | |
| 89 | return Math.round(value); | ||
| 90 | }; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Returns the sum of all numbers in the given array. | ||
| 94 | * | ||
| 95 | * ```handlebars | ||
| 96 | * {{sum "[1, 2, 3, 4, 5]"}} | ||
| 97 | * //=> '15' | ||
| 98 | * ``` | ||
| 99 | * | ||
| 100 | * @name .sum | ||
| 101 | * @param {Array} `array` Array of numbers to add up. | ||
| 102 | * @return {Number} | ||
| 103 | * @api public | ||
| 104 | */ | ||
| 105 | |||
| 106 | 1 | helpers.sum = function() { | |
| 107 | var args = utils.flatten([].concat.apply([], arguments)); | ||
| 108 | 7 | var i = args.length, sum = 0; | |
| 109 | 7 | while (i--) { | |
| 110 | 30 | if (!utils.isNumber(args[i])) { | |
| 111 | 4 | continue; | |
| 112 | } | ||
| 113 | 26 | sum += (+args[i]); | |
| 114 | } | ||
| 115 | 7 | return sum; | |
| 116 | }; | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Returns the average of all numbers in the given array. | ||
| 120 | * | ||
| 121 | * ```handlebars | ||
| 122 | * {{avg "[1, 2, 3, 4, 5]"}} | ||
| 123 | * //=> '3' | ||
| 124 | * ``` | ||
| 125 | * | ||
| 126 | * @name .avg | ||
| 127 | * @param {Array} `array` Array of numbers to add up. | ||
| 128 | * @return {Number} | ||
| 129 | * @api public | ||
| 130 | */ | ||
| 131 | |||
| 132 | 1 | helpers.avg = function() { | |
| 133 | var args = utils.flatten([].concat.apply([], arguments)); | ||
| 134 | // remove handlebars options object | ||
| 135 | 3 | args.pop(); | |
| 136 | 3 | return exports.sum(args) / args.length; | |
| 137 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Expose `helpers` | ||
| 5 | */ | ||
| 6 | |||
| 7 | 1 | var helpers = module.exports; | |
| 8 | |||
| 9 | /** | ||
| 10 | * Returns the first value if defined, otherwise the "default" value is returned. | ||
| 11 | * | ||
| 12 | * @param {any} `value` | ||
| 13 | * @param {any} `defaultValue` | ||
| 14 | * @return {String} | ||
| 15 | * @alias .or | ||
| 16 | * @api public | ||
| 17 | */ | ||
| 18 | |||
| 19 | 1 | helpers.default = function(...args) { | |
| 20 | args.pop(); | ||
| 21 | 3 | const value = args.shift(); | |
| 22 | 3 | const defaultValue = args.shift(); | |
| 23 | 3 | return !value | |
| 24 | ? defaultValue | ||
| 25 | : value; | ||
| 26 | }; | ||
| 27 | |||
| 28 | |||
| 29 | /** | ||
| 30 | * Block helper that renders the block without taking any arguments. | ||
| 31 | * | ||
| 32 | * @return {String} | ||
| 33 | * @block | ||
| 34 | * @api public | ||
| 35 | */ | ||
| 36 | |||
| 37 | 1 | helpers.noop = function() { | |
| 38 | const options = arguments[arguments.length - 1]; | ||
| 39 | 1 | return options.fn(this); | |
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Block helper that builds the context for the block | ||
| 44 | * from the options hash. | ||
| 45 | * | ||
| 46 | * @contributor Vladimir Kuznetsov <https://github.com/mistakster> | ||
| 47 | * @block | ||
| 48 | * @api public | ||
| 49 | */ | ||
| 50 | |||
| 51 | 1 | helpers.withHash = function() { | |
| 52 | const options = arguments[arguments.length - 1]; | ||
| 53 | if ( options.hash && Object.keys(options.hash).length) { |
||
| 54 | 3 | return options.fn(options.hash); | |
| 55 | } else { | ||
| 56 | 3 | return options.inverse(this); | |
| 57 | } | ||
| 58 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * Add commas to numbers | ||
| 13 | * | ||
| 14 | * @param {Number} `num` | ||
| 15 | * @return {Number} | ||
| 16 | * @api public | ||
| 17 | */ | ||
| 18 | |||
| 19 | 1 | helpers.addCommas = function(num) { | |
| 20 | return num.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); | ||
| 21 | }; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Convert a string or number to a formatted phone number. | ||
| 25 | * | ||
| 26 | * @param {Number|String} `num` The phone number to format, e.g. `8005551212` | ||
| 27 | * @return {Number} Formatted phone number: `(800) 555-1212` | ||
| 28 | * @source http://bit.ly/QlPmPr | ||
| 29 | * @api public | ||
| 30 | */ | ||
| 31 | |||
| 32 | 1 | helpers.phoneNumber = function(num) { | |
| 33 | num = num.toString(); | ||
| 34 | |||
| 35 | 1 | return '(' + num.substr(0, 3) + ') ' | |
| 36 | + num.substr(3, 3) + '-' | ||
| 37 | + num.substr(6, 4); | ||
| 38 | }; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Generate a random number between two values | ||
| 42 | * | ||
| 43 | * @param {Number} `min` | ||
| 44 | * @param {Number} `max` | ||
| 45 | * @contributor Tim Douglas <https://github.com/timdouglas> | ||
| 46 | * @return {String} | ||
| 47 | * @api public | ||
| 48 | */ | ||
| 49 | |||
| 50 | 1 | helpers.random = function(min, max) { | |
| 51 | return Math.floor(Math.random() * (max - min + 1)) + min; | ||
| 52 | }; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Abbreviate numbers to the given number of `precision`. This is for | ||
| 56 | * general numbers, not size in bytes. | ||
| 57 | * | ||
| 58 | * @param {Number} `number` | ||
| 59 | * @param {Number} `precision` | ||
| 60 | * @return {String} | ||
| 61 | * @api public | ||
| 62 | */ | ||
| 63 | |||
| 64 | 1 | helpers.toAbbr = function(number, precision) { | |
| 65 | if ( !utils.isNumber(number) ) { |
||
| 66 | number = 0; | ||
| 67 | } | ||
| 68 | 8 | if (utils.isUndefined(precision)) { | |
| 69 | 6 | precision = 2; | |
| 70 | } | ||
| 71 | |||
| 72 | 8 | number = +number; | |
| 73 | // 2 decimal places => 100, 3 => 1000, etc. | ||
| 74 | 8 | precision = Math.pow(10, precision); | |
| 75 | 8 | var abbr = ['k', 'm', 'b', 't', 'q']; | |
| 76 | 8 | var len = abbr.length - 1; | |
| 77 | |||
| 78 | while ( len >= 0 ) { |
||
| 79 | 26 | var size = Math.pow(10, (len + 1) * 3); | |
| 80 | 26 | if (size <= (number + 1)) { | |
| 81 | 8 | number = Math.round(number * precision / size) / precision; | |
| 82 | 8 | number += abbr[len]; | |
| 83 | 8 | break; | |
| 84 | } | ||
| 85 | 18 | len--; | |
| 86 | } | ||
| 87 | 8 | return number; | |
| 88 | }; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Returns a string representing the given number in exponential notation. | ||
| 92 | * | ||
| 93 | * ```js | ||
| 94 | * {{toExponential number digits}}; | ||
| 95 | * ``` | ||
| 96 | * @param {Number} `number` | ||
| 97 | * @param {Number} `fractionDigits` Optional. An integer specifying the number of digits to use after the decimal point. Defaults to as many digits as necessary to specify the number. | ||
| 98 | * @return {Number} | ||
| 99 | * @api public | ||
| 100 | */ | ||
| 101 | |||
| 102 | 1 | helpers.toExponential = function(number, digits) { | |
| 103 | if ( !utils.isNumber(number) ) { |
||
| 104 | number = 0; | ||
| 105 | } | ||
| 106 | 2 | if (utils.isUndefined(digits)) { | |
| 107 | 1 | digits = 0; | |
| 108 | } | ||
| 109 | 2 | number = +number; | |
| 110 | 2 | return number.toExponential(digits); | |
| 111 | }; | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Formats the given number using fixed-point notation. | ||
| 115 | * | ||
| 116 | * @param {Number} `number` | ||
| 117 | * @param {Number} `digits` Optional. The number of digits to use after the decimal point; this may be a value between 0 and 20, inclusive, and implementations may optionally support a larger range of values. If this argument is omitted, it is treated as 0. | ||
| 118 | * @return {Number} | ||
| 119 | * @api public | ||
| 120 | */ | ||
| 121 | |||
| 122 | 1 | helpers.toFixed = function(number, digits) { | |
| 123 | if ( !utils.isNumber(number) ) { |
||
| 124 | number = 0; | ||
| 125 | } | ||
| 126 | 2 | if (utils.isUndefined(digits)) { | |
| 127 | 1 | digits = 0; | |
| 128 | } | ||
| 129 | 2 | number = +number; | |
| 130 | 2 | return number.toFixed(digits); | |
| 131 | }; | ||
| 132 | |||
| 133 | /** | ||
| 134 | * @param {Number} `number` | ||
| 135 | * @return {Number} | ||
| 136 | * @api public | ||
| 137 | */ | ||
| 138 | |||
| 139 | 1 | helpers.toFloat = function(number) { | |
| 140 | return parseFloat(number); | ||
| 141 | }; | ||
| 142 | |||
| 143 | /** | ||
| 144 | * @param {Number} `number` | ||
| 145 | * @return {Number} | ||
| 146 | * @api public | ||
| 147 | */ | ||
| 148 | |||
| 149 | 1 | helpers.toInt = function(number) { | |
| 150 | return parseInt(number, 10); | ||
| 151 | }; | ||
| 152 | |||
| 153 | /** | ||
| 154 | * @param {Number} `number` | ||
| 155 | * @param {Number} `precision` Optional. The number of significant digits. | ||
| 156 | * @return {Number} | ||
| 157 | * @api public | ||
| 158 | */ | ||
| 159 | |||
| 160 | 1 | helpers.toPrecision = function(number, precision) { | |
| 161 | if ( !utils.isNumber(number) ) { |
||
| 162 | number = 0; | ||
| 163 | } | ||
| 164 | 2 | if (utils.isUndefined(precision)) { | |
| 165 | 1 | precision = 1; | |
| 166 | } | ||
| 167 | 2 | number = +number; | |
| 168 | 2 | return number.toPrecision(precision); | |
| 169 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var hasOwn = Object.hasOwnProperty; | |
| 4 | 1 | var utils = require('./utils/'); | |
| 5 | |||
| 6 | /** | ||
| 7 | * Expose `helpers` | ||
| 8 | */ | ||
| 9 | |||
| 10 | 1 | var helpers = module.exports; | |
| 11 | |||
| 12 | /** | ||
| 13 | * Extend the context with the properties of other objects. | ||
| 14 | * A shallow merge is performed to avoid mutating the context. | ||
| 15 | * | ||
| 16 | * @param {Object} `objects` One or more objects to extend. | ||
| 17 | * @return {Object} | ||
| 18 | * @api public | ||
| 19 | */ | ||
| 20 | |||
| 21 | 1 | helpers.extend = function(/*objects*/) { | |
| 22 | var args = [].slice.call(arguments); | ||
| 23 | 3 | var last = args[args.length - 1]; | |
| 24 | |||
| 25 | if ( last && utils.isObject(last) && last.hash) { |
||
| 26 | 1 | last = last.hash; | |
| 27 | 1 | args.pop(); // remove handlebars options object | |
| 28 | 1 | args.push(last); | |
| 29 | } | ||
| 30 | |||
| 31 | 3 | var len = args.length; | |
| 32 | 3 | var context = {}; | |
| 33 | 3 | var i = -1; | |
| 34 | |||
| 35 | 3 | while (++i < len) { | |
| 36 | 10 | var obj = args[i]; | |
| 37 | 10 | if (utils.isObject(obj)) { | |
| 38 | 9 | for (var key in obj) { | |
| 39 | if ( Object.hasOwnProperty.call(obj, key) ) { |
||
| 40 | 8 | context[key] = obj[key]; | |
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | 3 | return context; | |
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Block helper that iterates over the properties of | ||
| 50 | * an object, exposing each key and value on the context. | ||
| 51 | * | ||
| 52 | * @name .forIn | ||
| 53 | * @param {Object} `context` | ||
| 54 | * @return {String} | ||
| 55 | * @block | ||
| 56 | * @api public | ||
| 57 | */ | ||
| 58 | |||
| 59 | 1 | helpers.forIn = function(obj) { | |
| 60 | const options = arguments[arguments.length - 1]; | ||
| 61 | 3 | if (utils.isOptions(obj)) { | |
| 62 | 1 | return obj.inverse(this); | |
| 63 | } | ||
| 64 | |||
| 65 | 2 | var data = utils.createFrame(options, options.hash); | |
| 66 | 2 | var result = ''; | |
| 67 | |||
| 68 | 2 | for (var key in obj) { | |
| 69 | 4 | data.key = key; | |
| 70 | 4 | result += options.fn(obj[key], {data: data}); | |
| 71 | } | ||
| 72 | 2 | return result; | |
| 73 | }; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Block helper that iterates over the **own** properties of | ||
| 77 | * an object, exposing each key and value on the context. | ||
| 78 | * | ||
| 79 | * @name .forOwn | ||
| 80 | * @param {Object} `obj` The object to iterate over. | ||
| 81 | * @return {String} | ||
| 82 | * @block | ||
| 83 | * @api public | ||
| 84 | */ | ||
| 85 | |||
| 86 | 1 | helpers.forOwn = function(obj) { | |
| 87 | const options = arguments[arguments.length - 1]; | ||
| 88 | 6 | if (utils.isOptions(obj)) { | |
| 89 | 1 | return obj.inverse(this); | |
| 90 | } | ||
| 91 | |||
| 92 | 5 | var data = utils.createFrame(options, options.hash); | |
| 93 | 5 | var result = ''; | |
| 94 | |||
| 95 | 5 | for (var key in obj) { | |
| 96 | 13 | if (Object.hasOwnProperty.call(obj, key)) { | |
| 97 | 12 | data.key = key; | |
| 98 | 12 | result += options.fn(obj[key], {data: data}); | |
| 99 | } | ||
| 100 | } | ||
| 101 | 5 | return result; | |
| 102 | }; | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Take arguments and, if they are string or number, convert them to a dot-delineated object property path. | ||
| 106 | * | ||
| 107 | * @name .toPath | ||
| 108 | * @param {String|Number} `prop` The property segments to assemble (can be multiple). | ||
| 109 | * @return {String} | ||
| 110 | * @api public | ||
| 111 | */ | ||
| 112 | 1 | helpers.toPath = function (/*prop*/) { | |
| 113 | var prop = []; | ||
| 114 | 3 | for (var i = 0; i < arguments.length; i++) { | |
| 115 | 12 | if (typeof arguments[i] === "string" || typeof arguments[i] === "number") { | |
| 116 | 9 | prop.push(arguments[i]); | |
| 117 | } | ||
| 118 | } | ||
| 119 | 3 | return prop.join('.'); | |
| 120 | }; | ||
| 121 | |||
| 122 | |||
| 123 | /** | ||
| 124 | * Return true if `key` is an own, enumerable property | ||
| 125 | * of the given `context` object. | ||
| 126 | * | ||
| 127 | * ```handlebars | ||
| 128 | * {{hasOwn context key}} | ||
| 129 | * ``` | ||
| 130 | * | ||
| 131 | * @name .hasOwn | ||
| 132 | * @param {String} `key` | ||
| 133 | * @param {Object} `context` The context object. | ||
| 134 | * @return {Boolean} | ||
| 135 | * @api public | ||
| 136 | */ | ||
| 137 | |||
| 138 | 1 | helpers.hasOwn = function(context, key) { | |
| 139 | return hasOwn.call(context, key); | ||
| 140 | }; | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Return true if `value` is an object. | ||
| 144 | * | ||
| 145 | * ```handlebars | ||
| 146 | * {{isObject "foo"}} | ||
| 147 | * //=> false | ||
| 148 | * ``` | ||
| 149 | * @name .isObject | ||
| 150 | * @param {String} `value` | ||
| 151 | * @return {Boolean} | ||
| 152 | * @api public | ||
| 153 | */ | ||
| 154 | |||
| 155 | 1 | helpers.isObject = function(value) { | |
| 156 | return value && typeof value === 'object' |
||
| 157 | && !Array.isArray(value); | ||
| 158 | }; | ||
| 159 | |||
| 160 | /** | ||
| 161 | * Deeply merge the properties of the given `objects` with the | ||
| 162 | * context object. | ||
| 163 | * | ||
| 164 | * @name .merge | ||
| 165 | * @param {Object} `object` The target object. Pass an empty object to shallow clone. | ||
| 166 | * @param {Object} `objects` | ||
| 167 | * @return {Object} | ||
| 168 | * @api public | ||
| 169 | */ | ||
| 170 | |||
| 171 | 1 | helpers.merge = function(context/*, objects, options*/) { | |
| 172 | var args = [].slice.call(arguments); | ||
| 173 | 1 | var last = args[args.length - 1]; | |
| 174 | |||
| 175 | if ( last && typeof last === 'object' && last.hash) { |
||
| 176 | 1 | last = last.hash; | |
| 177 | 1 | args.pop(); // remove handlebars options object | |
| 178 | 1 | args.push(last); | |
| 179 | } | ||
| 180 | |||
| 181 | 1 | context = utils.merge.apply(utils.merge, args); | |
| 182 | 1 | return context; | |
| 183 | }; | ||
| 184 | |||
| 185 | /** | ||
| 186 | * Block helper that parses a string using `JSON.parse`, | ||
| 187 | * then passes the parsed object to the block as context. | ||
| 188 | * | ||
| 189 | * @param {String} `string` The string to parse | ||
| 190 | * @contributor github.com/keeganstreet | ||
| 191 | * @block | ||
| 192 | * @api public | ||
| 193 | */ | ||
| 194 | |||
| 195 | 1 | helpers.JSONparse = function(str) { | |
| 196 | const options = arguments[arguments.length - 1]; | ||
| 197 | 4 | return options.fn(JSON.parse(str)); | |
| 198 | }; | ||
| 199 | |||
| 200 | /** | ||
| 201 | * Stringify an object using `JSON.stringify`. | ||
| 202 | * | ||
| 203 | * @param {Object} `obj` Object to stringify | ||
| 204 | * @return {String} | ||
| 205 | * @api public | ||
| 206 | */ | ||
| 207 | |||
| 208 | 1 | helpers.JSONstringify = function(obj, indent) { | |
| 209 | if (!utils.isNumber(indent)) { | ||
| 210 | 4 | indent = 0; | |
| 211 | } | ||
| 212 | 5 | return JSON.stringify(obj, null, indent); | |
| 213 | }; | ||
| 214 | |||
| 215 | /** | ||
| 216 | * Alias for JSONstringify. this will be | ||
| 217 | * deprecated in a future release | ||
| 218 | */ | ||
| 219 | |||
| 220 | 1 | helpers.stringify = helpers.JSONstringify; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./utils'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * camelCase the characters in the given `string`. | ||
| 13 | * | ||
| 14 | * ```js | ||
| 15 | * {{camelcase "foo bar baz"}}; | ||
| 16 | * //=> 'fooBarBaz' | ||
| 17 | * ``` | ||
| 18 | * | ||
| 19 | * @name .camelcase | ||
| 20 | * @param {String} `string` The string to camelcase. | ||
| 21 | * @return {String} | ||
| 22 | * @api public | ||
| 23 | */ | ||
| 24 | |||
| 25 | 1 | helpers.camelcase = function(str) { | |
| 26 | return utils.changecase(str, function(ch) { | ||
| 27 | return ch.toUpperCase(); | ||
| 28 | }); | ||
| 29 | }; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Capitalize the first word in a sentence. | ||
| 33 | * | ||
| 34 | * ```handlebars | ||
| 35 | * {{capitalize "foo bar baz"}} | ||
| 36 | * //=> "Foo bar baz" | ||
| 37 | * ``` | ||
| 38 | * @param {String} `str` | ||
| 39 | * @return {String} | ||
| 40 | * @api public | ||
| 41 | */ | ||
| 42 | |||
| 43 | 1 | helpers.capitalize = function(str) { | |
| 44 | if ( str && typeof str === 'string') { |
||
| 45 | 18 | return str.charAt(0).toUpperCase() | |
| 46 | + str.slice(1); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Capitalize all words in a string. | ||
| 52 | * | ||
| 53 | * ```handlebars | ||
| 54 | * {{capitalizeAll "foo bar baz"}} | ||
| 55 | * //=> "Foo Bar Baz" | ||
| 56 | * ``` | ||
| 57 | * @param {String} `str` | ||
| 58 | * @return {String} | ||
| 59 | * @api public | ||
| 60 | */ | ||
| 61 | |||
| 62 | 1 | helpers.capitalizeAll = function(str) { | |
| 63 | if ( str && typeof str === 'string') { |
||
| 64 | 1 | return str.replace(/\w\S*/g, function(word) { | |
| 65 | return exports.capitalize(word); | ||
| 66 | }); | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Center a string using non-breaking spaces | ||
| 72 | * | ||
| 73 | * @param {String} `str` | ||
| 74 | * @param {String} `spaces` | ||
| 75 | * @return {String} | ||
| 76 | * @api public | ||
| 77 | */ | ||
| 78 | |||
| 79 | 1 | helpers.center = function(str, spaces) { | |
| 80 | if ( str && typeof str === 'string') { |
||
| 81 | 1 | var space = ''; | |
| 82 | 1 | var i = 0; | |
| 83 | 1 | while (i < spaces) { | |
| 84 | 2 | space += ' '; | |
| 85 | 2 | i++; | |
| 86 | } | ||
| 87 | 1 | return space + str + space; | |
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Like trim, but removes both extraneous whitespace **and | ||
| 93 | * non-word characters** from the beginning and end of a string. | ||
| 94 | * | ||
| 95 | * ```js | ||
| 96 | * {{chop "_ABC_"}} | ||
| 97 | * //=> 'ABC' | ||
| 98 | * | ||
| 99 | * {{chop "-ABC-"}} | ||
| 100 | * //=> 'ABC' | ||
| 101 | * | ||
| 102 | * {{chop " ABC "}} | ||
| 103 | * //=> 'ABC' | ||
| 104 | * ``` | ||
| 105 | * | ||
| 106 | * @name .chop | ||
| 107 | * @param {String} `string` The string to chop. | ||
| 108 | * @return {String} | ||
| 109 | * @api public | ||
| 110 | */ | ||
| 111 | |||
| 112 | 1 | helpers.chop = function(str) { | |
| 113 | return utils.chop(str); | ||
| 114 | }; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * dash-case the characters in `string`. Replaces non-word | ||
| 118 | * characters and periods with hyphens. | ||
| 119 | * | ||
| 120 | * ```js | ||
| 121 | * {{dashcase "a-b-c d_e"}} | ||
| 122 | * //=> 'a-b-c-d-e' | ||
| 123 | * ``` | ||
| 124 | * | ||
| 125 | * @param {String} `string` | ||
| 126 | * @return {String} | ||
| 127 | * @api public | ||
| 128 | */ | ||
| 129 | |||
| 130 | 1 | helpers.dashcase = function(str) { | |
| 131 | return utils.changecase(str, function(ch) { | ||
| 132 | return '-' + ch; | ||
| 133 | }); | ||
| 134 | }; | ||
| 135 | |||
| 136 | /** | ||
| 137 | * dot.case the characters in `string`. | ||
| 138 | * | ||
| 139 | * ```js | ||
| 140 | * {{dotcase "a-b-c d_e"}} | ||
| 141 | * //=> 'a.b.c.d.e' | ||
| 142 | * ``` | ||
| 143 | * | ||
| 144 | * @param {String} `string` | ||
| 145 | * @return {String} | ||
| 146 | * @api public | ||
| 147 | */ | ||
| 148 | |||
| 149 | 1 | helpers.dotcase = function(str) { | |
| 150 | return utils.changecase(str, function(ch) { | ||
| 151 | return '.' + ch; | ||
| 152 | }); | ||
| 153 | }; | ||
| 154 | |||
| 155 | /** | ||
| 156 | * Replace spaces in a string with hyphens. | ||
| 157 | * | ||
| 158 | * ```handlebars | ||
| 159 | * {{hyphenate "foo bar baz qux"}} | ||
| 160 | * //=> "foo-bar-baz-qux" | ||
| 161 | * ``` | ||
| 162 | * @param {String} `str` | ||
| 163 | * @return {String} | ||
| 164 | * @api public | ||
| 165 | */ | ||
| 166 | |||
| 167 | 1 | helpers.hyphenate = function(str) { | |
| 168 | if ( str && typeof str === 'string') { |
||
| 169 | 2 | return str.split(' ').join('-'); | |
| 170 | } | ||
| 171 | }; | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Return true if `value` is a string. | ||
| 175 | * | ||
| 176 | * ```handlebars | ||
| 177 | * {{isString "foo"}} | ||
| 178 | * //=> 'true' | ||
| 179 | * ``` | ||
| 180 | * @param {String} `value` | ||
| 181 | * @return {Boolean} | ||
| 182 | * @api public | ||
| 183 | */ | ||
| 184 | |||
| 185 | 1 | helpers.isString = function(value) { | |
| 186 | return utils.isString(value); | ||
| 187 | }; | ||
| 188 | |||
| 189 | /** | ||
| 190 | * Lowercase all characters in the given string. | ||
| 191 | * | ||
| 192 | * ```handlebars | ||
| 193 | * {{lowercase "Foo BAR baZ"}} | ||
| 194 | * //=> 'foo bar baz' | ||
| 195 | * ``` | ||
| 196 | * @param {String} `str` | ||
| 197 | * @return {String} | ||
| 198 | * @api public | ||
| 199 | */ | ||
| 200 | |||
| 201 | 1 | helpers.lowercase = function(str) { | |
| 202 | if ( str && typeof str === 'string') { |
||
| 203 | 1 | return str.toLowerCase(); | |
| 204 | } | ||
| 205 | }; | ||
| 206 | |||
| 207 | /** | ||
| 208 | * PascalCase the characters in `string`. | ||
| 209 | * | ||
| 210 | * ```js | ||
| 211 | * {{pascalcase "foo bar baz"}} | ||
| 212 | * //=> 'FooBarBaz' | ||
| 213 | * ``` | ||
| 214 | * | ||
| 215 | * @name .pascalcase | ||
| 216 | * @param {String} `string` | ||
| 217 | * @return {String} | ||
| 218 | * @api public | ||
| 219 | */ | ||
| 220 | |||
| 221 | 1 | helpers.pascalcase = function(str) { | |
| 222 | str = utils.changecase(str, function(ch) { | ||
| 223 | return ch.toUpperCase(); | ||
| 224 | }); | ||
| 225 | 4 | return str.charAt(0).toUpperCase() | |
| 226 | + str.slice(1); | ||
| 227 | }; | ||
| 228 | |||
| 229 | /** | ||
| 230 | * path/case the characters in `string`. | ||
| 231 | * | ||
| 232 | * ```js | ||
| 233 | * {{pathcase "a-b-c d_e"}} | ||
| 234 | * //=> 'a/b/c/d/e' | ||
| 235 | * ``` | ||
| 236 | * | ||
| 237 | * @param {String} `string` | ||
| 238 | * @return {String} | ||
| 239 | * @api public | ||
| 240 | */ | ||
| 241 | |||
| 242 | 1 | helpers.pathcase = function(str) { | |
| 243 | return utils.changecase(str, function(ch) { | ||
| 244 | return '/' + ch; | ||
| 245 | }); | ||
| 246 | }; | ||
| 247 | |||
| 248 | /** | ||
| 249 | * Replace spaces in the given string with pluses. | ||
| 250 | * | ||
| 251 | * ```handlebars | ||
| 252 | * {{plusify "foo bar baz"}} | ||
| 253 | * //=> 'foo+bar+baz' | ||
| 254 | * ``` | ||
| 255 | * @param {String} `str` The input string | ||
| 256 | * @return {String} Input string with spaces replaced by plus signs | ||
| 257 | * @source Stephen Way <https://github.com/stephenway> | ||
| 258 | * @api public | ||
| 259 | */ | ||
| 260 | |||
| 261 | 1 | helpers.plusify = function(str) { | |
| 262 | if (str && typeof str === 'string') { | ||
| 263 | 2 | return str.split(' ').join('+'); | |
| 264 | } | ||
| 265 | }; | ||
| 266 | |||
| 267 | /** | ||
| 268 | * Reverse a string. | ||
| 269 | * | ||
| 270 | * ```handlebars | ||
| 271 | * {{reverse "abcde"}} | ||
| 272 | * //=> 'edcba' | ||
| 273 | * ``` | ||
| 274 | * @name .reverse | ||
| 275 | * @param {String} `str` | ||
| 276 | * @return {String} | ||
| 277 | * @api public | ||
| 278 | */ | ||
| 279 | |||
| 280 | 1 | helpers.reverse = function(str) { | |
| 281 | if ( str && typeof str === 'string') { |
||
| 282 | 1 | return str.split('').reverse().join(''); | |
| 283 | } | ||
| 284 | }; | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Replace all occurrences of `a` with `b`. | ||
| 288 | * | ||
| 289 | * ```handlebars | ||
| 290 | * {{replace "a b a b a b" "a" "z"}} | ||
| 291 | * //=> 'z b z b z b' | ||
| 292 | * ``` | ||
| 293 | * @param {String} `str` | ||
| 294 | * @param {String} `a` | ||
| 295 | * @param {String} `b` | ||
| 296 | * @return {String} | ||
| 297 | * @api public | ||
| 298 | */ | ||
| 299 | |||
| 300 | 1 | helpers.replace = function(str, a, b) { | |
| 301 | if ( str && typeof str === 'string') { |
||
| 302 | if ( !a || typeof a !== 'string') {return str;} |
||
| 303 | if ( !b || typeof b !== 'string') {b = '';} |
||
| 304 | 2 | return str.split(a).join(b); | |
| 305 | } | ||
| 306 | }; | ||
| 307 | |||
| 308 | /** | ||
| 309 | * Sentence case the given string | ||
| 310 | * | ||
| 311 | * ```handlebars | ||
| 312 | * {{sentence "hello world. goodbye world."}} | ||
| 313 | * //=> 'Hello world. Goodbye world.' | ||
| 314 | * ``` | ||
| 315 | * @param {String} `str` | ||
| 316 | * @return {String} | ||
| 317 | * @api public | ||
| 318 | */ | ||
| 319 | |||
| 320 | 1 | helpers.sentence = function(str) { | |
| 321 | if ( str && typeof str === 'string') { |
||
| 322 | 1 | var re = /((?:\S[^\.\?\!]*)[\.\?\!]*)/g; | |
| 323 | 1 | return str.replace(re, function(txt) { | |
| 324 | return txt.charAt(0).toUpperCase() | ||
| 325 | + txt.substr(1).toLowerCase(); | ||
| 326 | }); | ||
| 327 | } | ||
| 328 | }; | ||
| 329 | |||
| 330 | /** | ||
| 331 | * snake_case the characters in the given `string`. | ||
| 332 | * | ||
| 333 | * ```js | ||
| 334 | * {{snakecase "a-b-c d_e"}} | ||
| 335 | * //=> 'a_b_c_d_e' | ||
| 336 | * ``` | ||
| 337 | * | ||
| 338 | * @param {String} `string` | ||
| 339 | * @return {String} | ||
| 340 | * @api public | ||
| 341 | */ | ||
| 342 | |||
| 343 | 1 | helpers.snakecase = function(str) { | |
| 344 | return utils.changecase(str, function(ch) { | ||
| 345 | return '_' + ch; | ||
| 346 | }); | ||
| 347 | }; | ||
| 348 | |||
| 349 | /** | ||
| 350 | * Split `string` by the given `character`. | ||
| 351 | * | ||
| 352 | * ```js | ||
| 353 | * {{split "a,b,c" ","}} | ||
| 354 | * //=> ['a', 'b', 'c'] | ||
| 355 | * ``` | ||
| 356 | * | ||
| 357 | * @param {String} `string` The string to split. | ||
| 358 | * @return {String} `character` Default is `,` | ||
| 359 | * @api public | ||
| 360 | */ | ||
| 361 | |||
| 362 | 1 | helpers.split = function(str, ch) { | |
| 363 | 1 | if (!helpers.isString(str)) {return '';} | |
| 364 | 3 | if (typeof ch !== 'string') {ch = ',';} | |
| 365 | 2 | return str.split(ch); | |
| 366 | }; | ||
| 367 | |||
| 368 | /** | ||
| 369 | * Tests whether a string begins with the given prefix. | ||
| 370 | * | ||
| 371 | * ```handlebars | ||
| 372 | * {{#startsWith "Goodbye" "Hello, world!"}} | ||
| 373 | * Whoops | ||
| 374 | * {{else}} | ||
| 375 | * Bro, do you even hello world? | ||
| 376 | * {{/startsWith}} | ||
| 377 | * ``` | ||
| 378 | * @param {String} `prefix` | ||
| 379 | * @param {String} `testString` | ||
| 380 | * @contributor Dan Fox <http://github.com/iamdanfox> | ||
| 381 | * @return {String} | ||
| 382 | * @block | ||
| 383 | * @api public | ||
| 384 | */ | ||
| 385 | |||
| 386 | 1 | helpers.startsWith = function(prefix, str) { | |
| 387 | const options = arguments[arguments.length - 1]; | ||
| 388 | 4 | if (str && typeof str === 'string') { | |
| 389 | 2 | if (str.indexOf(prefix) === 0) { | |
| 390 | 1 | return options.fn(this); | |
| 391 | } | ||
| 392 | } | ||
| 393 | 3 | if (typeof options.inverse === 'function') { | |
| 394 | 2 | return options.inverse(this); | |
| 395 | } | ||
| 396 | 1 | return ''; | |
| 397 | }; | ||
| 398 | |||
| 399 | /** | ||
| 400 | * Title case the given string. | ||
| 401 | * | ||
| 402 | * ```handlebars | ||
| 403 | * {{titleize "this is title case"}} | ||
| 404 | * //=> 'This Is Title Case' | ||
| 405 | * ``` | ||
| 406 | * @param {String} `str` | ||
| 407 | * @return {String} | ||
| 408 | * @api public | ||
| 409 | */ | ||
| 410 | |||
| 411 | 1 | helpers.titleize = function(str) { | |
| 412 | if ( str && typeof str === 'string') { |
||
| 413 | 2 | var title = str.replace(/[ \-_]+/g, ' '); | |
| 414 | 2 | var words = title.match(/\w+/g); | |
| 415 | 2 | if (!words) { | |
| 416 | 1 | return str; | |
| 417 | } | ||
| 418 | 1 | var len = words.length; | |
| 419 | 1 | var res = []; | |
| 420 | 1 | var i = 0; | |
| 421 | 1 | while (len--) { | |
| 422 | 7 | var word = words[i++]; | |
| 423 | 7 | res.push(exports.capitalize(word)); | |
| 424 | } | ||
| 425 | 1 | return res.join(' '); | |
| 426 | } | ||
| 427 | }; | ||
| 428 | |||
| 429 | /** | ||
| 430 | * Removes extraneous whitespace from the beginning and end | ||
| 431 | * of a string. | ||
| 432 | * | ||
| 433 | * ```js | ||
| 434 | * {{trim " ABC "}} | ||
| 435 | * //=> 'ABC' | ||
| 436 | * ``` | ||
| 437 | * | ||
| 438 | * @name .trim | ||
| 439 | * @param {String} `string` The string to trim. | ||
| 440 | * @return {String} | ||
| 441 | * @api public | ||
| 442 | */ | ||
| 443 | |||
| 444 | 1 | helpers.trim = function(str) { | |
| 445 | 1 | if (!helpers.isString(str)) {return '';} | |
| 446 | 2 | return str.trim(); | |
| 447 | }; | ||
| 448 | |||
| 449 | /** | ||
| 450 | * Uppercase all of the characters in the given string. If used as a | ||
| 451 | * block helper it will uppercase the entire block. This helper | ||
| 452 | * does not support inverse blocks. | ||
| 453 | * | ||
| 454 | * @name .uppercase | ||
| 455 | * @related capitalize capitalizeAll | ||
| 456 | * @param {String} `str` The string to uppercase | ||
| 457 | * @return {String} | ||
| 458 | * @block | ||
| 459 | * @api public | ||
| 460 | */ | ||
| 461 | |||
| 462 | 1 | helpers.uppercase = function(str) { | |
| 463 | const options = arguments[arguments.length - 1]; | ||
| 464 | if ( str && typeof str === 'string') { |
||
| 465 | 1 | return str.toUpperCase(); | |
| 466 | } | ||
| 467 | if ( options && typeof options === 'object' && options.fn) { |
||
| 468 | 1 | return options.fn(this).toUpperCase(); | |
| 469 | } | ||
| 470 | 1 | return ''; | |
| 471 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var url = require('url'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Expose `helpers` | ||
| 7 | */ | ||
| 8 | |||
| 9 | 1 | var helpers = module.exports; | |
| 10 | |||
| 11 | /** | ||
| 12 | * Encodes a Uniform Resource Identifier (URI) component | ||
| 13 | * by replacing each instance of certain characters by | ||
| 14 | * one, two, three, or four escape sequences representing | ||
| 15 | * the UTF-8 encoding of the character. | ||
| 16 | * | ||
| 17 | * @param {String} `str` The un-encoded string | ||
| 18 | * @return {String} The endcoded string | ||
| 19 | * @api public | ||
| 20 | */ | ||
| 21 | |||
| 22 | 1 | helpers.encodeURI = function(str) { | |
| 23 | return encodeURIComponent(str); | ||
| 24 | }; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Decode a Uniform Resource Identifier (URI) component. | ||
| 28 | * | ||
| 29 | * @param {String} `str` | ||
| 30 | * @return {String} | ||
| 31 | * @api public | ||
| 32 | */ | ||
| 33 | |||
| 34 | 1 | helpers.decodeURI = function(str) { | |
| 35 | return decodeURIComponent(str); | ||
| 36 | }; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Take a base URL, and a href URL, and resolve them as a | ||
| 40 | * browser would for an anchor tag. | ||
| 41 | * | ||
| 42 | * @param {String} `base` | ||
| 43 | * @param {String} `href` | ||
| 44 | * @return {String} | ||
| 45 | * @api public | ||
| 46 | */ | ||
| 47 | |||
| 48 | 1 | helpers.urlResolve = function(base, href) { | |
| 49 | return url.resolve(base, href); | ||
| 50 | }; | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Parses a `url` string into an object. | ||
| 54 | * | ||
| 55 | * @param {String} `str` URL string | ||
| 56 | * @return {String} Returns stringified JSON | ||
| 57 | * @api public | ||
| 58 | */ | ||
| 59 | |||
| 60 | 1 | helpers.urlParse = function(str) { | |
| 61 | return url.parse(str); | ||
| 62 | }; | ||
| 63 | |||
| 64 | |||
| 65 | /** | ||
| 66 | * Strip protocol from a `url`. | ||
| 67 | * | ||
| 68 | * Useful for displaying media that may have an 'http' protocol | ||
| 69 | * on secure connections. Will change 'http://foo.bar to `//foo.bar` | ||
| 70 | * | ||
| 71 | * @name .stripProtocol | ||
| 72 | * @param {String} `str` | ||
| 73 | * @return {String} the url with http protocol stripped | ||
| 74 | * @api public | ||
| 75 | */ | ||
| 76 | |||
| 77 | 1 | helpers.stripProtocol = function(str) { | |
| 78 | var parsed = url.parse(str); | ||
| 79 | 4 | delete parsed.protocol; | |
| 80 | 4 | return parsed.format(); | |
| 81 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const { getValue } = require('../../lib/common'); | |
| 4 | 1 | const getObject = require('../../../helpers/getObject'); | |
| 5 | |||
| 6 | 1 | module.exports = { | |
| 7 | sortBy: require('./lib/arraySort'), | ||
| 8 | filter: require('./lib/arrayFilter'), | ||
| 9 | flatten: require('./lib/arrayFlatten'), | ||
| 10 | iterator: require('./lib/makeIterator'), | ||
| 11 | indexOf: require('./lib/indexOf'), | ||
| 12 | typeOf: require('./lib/kindOf'), | ||
| 13 | isEven: require('./lib/isEven'), | ||
| 14 | isNumber: require('./lib/isNumber'), | ||
| 15 | isOdd: require('./lib/isOdd'), | ||
| 16 | createFrame: require('./lib/createFrame'), | ||
| 17 | get: getValue, | ||
| 18 | getObject: getObject[0].factory(), | ||
| 19 | forOwn: require('./lib/forOwn'), | ||
| 20 | merge: require('./lib/mixinDeep'), | ||
| 21 | deepMatches: require('./lib/deepMatches'), | ||
| 22 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var utils = require('./base'); | |
| 4 | |||
| 5 | /** | ||
| 6 | * Returns true if the given value is an object. | ||
| 7 | * | ||
| 8 | * ```js | ||
| 9 | * console.log(utils.isObject(null)); | ||
| 10 | * //=> false | ||
| 11 | * console.log(utils.isObject([])); | ||
| 12 | * //=> false | ||
| 13 | * console.log(utils.isObject(function() {})); | ||
| 14 | * //=> false | ||
| 15 | * console.log(utils.isObject({})); | ||
| 16 | * //=> true | ||
| 17 | * ``` | ||
| 18 | * @param {Object} `val` | ||
| 19 | * @return {Boolean} | ||
| 20 | * @api public | ||
| 21 | */ | ||
| 22 | 1 | utils.isObject = function (val) { | |
| 23 | return typeof val === 'object'; |
||
| 24 | }; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Returns true if the given value contains the given | ||
| 28 | * `object`, optionally passing a starting index. | ||
| 29 | * | ||
| 30 | * @param {Array} val | ||
| 31 | * @param {Object} obj | ||
| 32 | * @param {Number} start | ||
| 33 | * @return {Boolean} | ||
| 34 | */ | ||
| 35 | |||
| 36 | 1 | utils.contains = function (val, obj, start) { | |
| 37 | var len = val ? val.length : 0; |
||
| 38 | var idx = start < 0 | ||
| 39 | ? Math.max(0, len + start) | ||
| 40 | : start; | ||
| 41 | |||
| 42 | var res = false; | ||
| 43 | var i = 0; | ||
| 44 | |||
| 45 | start = idx || 0; | ||
| 46 | |||
| 47 | if (Array.isArray(val)) { | ||
| 48 | res = utils.indexOf(val, obj, start) > -1; | ||
| 49 | |||
| 50 | } else if (utils.isNumber(len)) { | ||
| 51 | res = (typeof val === 'string' | ||
| 52 | ? val.indexOf(obj, start) | ||
| 53 | : utils.indexOf(val, obj, start)) > -1; | ||
| 54 | |||
| 55 | } else { | ||
| 56 | utils.iterator(val, function (ele) { | ||
| 57 | if (start < i++) { |
||
| 58 | return !(res = (ele === obj)); | ||
| 59 | } |
||
| 60 | }); | ||
| 61 | } | ||
| 62 | return res; | ||
| 63 | }; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Converts a "regex-string" to an actual regular expression. | ||
| 67 | * | ||
| 68 | * ```js | ||
| 69 | * utils.toRegex('"/foo/"'); | ||
| 70 | * //=> /foo/ | ||
| 71 | * ``` | ||
| 72 | * @param {Object} `value` | ||
| 73 | * @return {Boolean} | ||
| 74 | * @api public | ||
| 75 | */ | ||
| 76 | |||
| 77 | 1 | utils.toRegex = function (val) { | |
| 78 | return new RegExp(val.replace(/^\/|\/$/g, '')); |
||
| 79 | }; | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Returns true if the given value appears to be a | ||
| 83 | * regular expression. | ||
| 84 | * | ||
| 85 | * @param {Object} `value` | ||
| 86 | * @return {Boolean} | ||
| 87 | * @api public | ||
| 88 | */ | ||
| 89 | |||
| 90 | 1 | utils.isRegex = function (val) { | |
| 91 | if (utils.typeOf(val) === 'regexp') { |
||
| 92 | return true; | ||
| 93 | } |
||
| 94 | if (typeof val !== 'string') { | ||
| 95 | return false; | ||
| 96 | } | ||
| 97 | return val.charAt(0) === '/' | ||
| 98 | && val.slice(-1) === '\/'; | ||
| 99 | }; | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Remove leading and trailing whitespace and non-word | ||
| 103 | * characters from the given string. | ||
| 104 | * | ||
| 105 | * @param {String} `str` | ||
| 106 | * @return {String} | ||
| 107 | */ | ||
| 108 | |||
| 109 | 1 | utils.chop = function (str) { | |
| 110 | 1 | if (!utils.isString(str)) { return ''; } | |
| 111 | 8 | var re = /^[-_.\W\s]+|[-_.\W\s]+$/g; | |
| 112 | 8 | return str.trim().replace(re, ''); | |
| 113 | }; | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Change casing on the given `string`, optionally | ||
| 117 | * passing a delimiter to use between words in the | ||
| 118 | * returned string. | ||
| 119 | * | ||
| 120 | * ```js | ||
| 121 | * utils.changecase('fooBarBaz'); | ||
| 122 | * //=> 'foo bar baz' | ||
| 123 | * | ||
| 124 | * utils.changecase('fooBarBaz' '-'); | ||
| 125 | * //=> 'foo-bar-baz' | ||
| 126 | * ``` | ||
| 127 | * @param {String} `string` The string to change. | ||
| 128 | * @return {String} | ||
| 129 | * @api public | ||
| 130 | */ | ||
| 131 | |||
| 132 | 1 | utils.changecase = function (str, fn) { | |
| 133 | 6 | if (!utils.isString(str)) { return ''; } | |
| 134 | 18 | if (str.length === 1) { | |
| 135 | 12 | return str.toLowerCase(); | |
| 136 | } | ||
| 137 | |||
| 138 | 6 | str = utils.chop(str).toLowerCase(); | |
| 139 | if ( typeof fn !== 'function' ) { |
||
| 140 | fn = utils.identity; | ||
| 141 | } | ||
| 142 | |||
| 143 | 6 | var re = /[-_.\W\s]+(\w|$)/g; | |
| 144 | 6 | return str.replace(re, function (_, ch) { | |
| 145 | return fn(ch); | ||
| 146 | }); | ||
| 147 | }; | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Generate a random number | ||
| 151 | * | ||
| 152 | * @param {Number} `min` | ||
| 153 | * @param {Number} `max` | ||
| 154 | * @return {Number} | ||
| 155 | * @api public | ||
| 156 | */ | ||
| 157 | |||
| 158 | 1 | utils.random = function (min, max) { | |
| 159 | return min + Math.floor(Math.random() * (max - min + 1)); |
||
| 160 | }; | ||
| 161 | |||
| 162 | /** | ||
| 163 | * Returns true if the given value is `undefined` or | ||
| 164 | * is a handlebars options hash. | ||
| 165 | * | ||
| 166 | * @param {any} `value` | ||
| 167 | * @return {Boolean} | ||
| 168 | * @api public | ||
| 169 | */ | ||
| 170 | |||
| 171 | 1 | utils.isUndefined = function (val) { | |
| 172 | return typeof val === 'undefined' | ||
| 173 | || (val && !!val.hash); | ||
| 174 | }; | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Returns true if the given value appears to be an **options** object. | ||
| 178 | * | ||
| 179 | * @param {Object} `value` | ||
| 180 | * @return {Boolean} | ||
| 181 | * @api public | ||
| 182 | */ | ||
| 183 | |||
| 184 | 1 | utils.isOptions = function (val) { | |
| 185 | return utils.isObject(val) && Object.hasOwnProperty.call(val, 'hash'); | ||
| 186 | }; | ||
| 187 | |||
| 188 | /** | ||
| 189 | * Get options from the options hash and `this`. | ||
| 190 | * | ||
| 191 | * @param {Object} `app` The current application instance. | ||
| 192 | * @return {Object} | ||
| 193 | * @api public | ||
| 194 | */ | ||
| 195 | |||
| 196 | 1 | utils.getArgs = function (app, args) { | |
| 197 | var opts = utils.merge({}, app && app.options); |
||
| 198 | if (!Array.isArray(args)) { | ||
| 199 | args = [].slice.call(args); | ||
| 200 | } | ||
| 201 | |||
| 202 | var last = args[args.length - 1]; | ||
| 203 | |||
| 204 | // merge `options.hash` into the options | ||
| 205 | if (utils.isOptions(last)) { | ||
| 206 | var hbsOptions = args.pop(); | ||
| 207 | opts = utils.get(opts, hbsOptions.name) || opts; | ||
| 208 | opts = utils.merge({}, opts, hbsOptions.hash); | ||
| 209 | |||
| 210 | // if the last arg is an object, merge it | ||
| 211 | // into the options | ||
| 212 | } else if (utils.isObject(last)) { | ||
| 213 | opts = utils.merge({}, opts, args.pop()); | ||
| 214 | } | ||
| 215 | |||
| 216 | args.push(opts); | ||
| 217 | return args; | ||
| 218 | }; | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Returns true if the given value is an object | ||
| 222 | * and not an array. | ||
| 223 | * | ||
| 224 | * @param {any} `value` | ||
| 225 | * @return {Boolean} | ||
| 226 | * @api public | ||
| 227 | */ | ||
| 228 | |||
| 229 | 1 | utils.isObject = function (val) { | |
| 230 | return val && typeof val === 'object' | ||
| 231 | && !Array.isArray(val); | ||
| 232 | }; | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Returns true if the given value is empty. | ||
| 236 | * | ||
| 237 | * @param {any} `value` | ||
| 238 | * @return {Boolean} | ||
| 239 | * @api public | ||
| 240 | */ | ||
| 241 | |||
| 242 | 1 | utils.isEmpty = function (val) { | |
| 243 | if (val === 0 || val === '0') { | ||
| 244 | 6 | return false; | |
| 245 | } | ||
| 246 | if (!val || (Array.isArray(val) && val.length === 0 )) { |
||
| 247 | 27 | return true; | |
| 248 | } | ||
| 249 | 111 | if (typeof val === 'object' && !Object.keys(val).length) { | |
| 250 | 13 | return true; | |
| 251 | } | ||
| 252 | 98 | return false; | |
| 253 | }; | ||
| 254 | |||
| 255 | /** | ||
| 256 | * Try to parse the given `string` as JSON. Fails | ||
| 257 | * gracefully if the value cannot be parsed. | ||
| 258 | * | ||
| 259 | * @name .tryParse | ||
| 260 | * @param {String} `string` | ||
| 261 | * @return {Object} | ||
| 262 | * @api public | ||
| 263 | */ | ||
| 264 | |||
| 265 | 1 | utils.tryParse = function (str) { | |
| 266 | try { | ||
| 267 | 7 | return JSON.parse(str); | |
| 268 | } catch (err) { } | ||
| 269 | 3 | return null; | |
| 270 | }; | ||
| 271 | |||
| 272 | /** | ||
| 273 | * Return the given value. If the value is a function | ||
| 274 | * it will be called, and the result is returned. | ||
| 275 | * | ||
| 276 | * @param {any} `val` | ||
| 277 | * @return {any} | ||
| 278 | * @api public | ||
| 279 | */ | ||
| 280 | |||
| 281 | 1 | utils.result = function (value) { | |
| 282 | if ( typeof value === 'function' ) { |
||
| 283 | return value(); | ||
| 284 | } | ||
| 285 | 6 | return value; | |
| 286 | }; | ||
| 287 | |||
| 288 | /** | ||
| 289 | * Return the given value, unchanged. | ||
| 290 | * | ||
| 291 | * @param {any} `val` | ||
| 292 | * @return {any} | ||
| 293 | * @api public | ||
| 294 | */ | ||
| 295 | |||
| 296 | 1 | utils.identity = function (val) { | |
| 297 | return val; |
||
| 298 | }; | ||
| 299 | |||
| 300 | /** | ||
| 301 | * Return true if `val` is a string. | ||
| 302 | * | ||
| 303 | * @param {any} `val` The value to check | ||
| 304 | * @return {Boolean} | ||
| 305 | * @api public | ||
| 306 | */ | ||
| 307 | |||
| 308 | 1 | utils.isString = function (val) { | |
| 309 | return val && typeof val === 'string'; | ||
| 310 | }; | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Cast `val` to an array. | ||
| 314 | * | ||
| 315 | * @param {any} `val` The value to arrayify. | ||
| 316 | * @return {Array} | ||
| 317 | * @api public | ||
| 318 | */ | ||
| 319 | |||
| 320 | 1 | utils.arrayify = function (val) { | |
| 321 | return val ? (Array.isArray(val) ? val : [val]) : []; |
||
| 322 | }; | ||
| 323 | |||
| 324 | 1 | utils.isArray = Array.isArray; | |
| 325 | |||
| 326 | /** | ||
| 327 | * Get the context to use for rendering. | ||
| 328 | * | ||
| 329 | * @param {Object} `thisArg` Optional invocation context `this` | ||
| 330 | * @return {Object} | ||
| 331 | * @api public | ||
| 332 | */ | ||
| 333 | |||
| 334 | 1 | utils.context = function (thisArg, locals, options) { | |
| 335 | if ( utils.isOptions(thisArg) ) { |
||
| 336 | return utils.context({}, locals, thisArg); | ||
| 337 | } | ||
| 338 | // ensure args are in the correct order | ||
| 339 | if ( utils.isOptions(locals) ) { |
||
| 340 | return utils.context(thisArg, options, locals); | ||
| 341 | } | ||
| 342 | var appContext = utils.isApp(thisArg) ? thisArg.context : {}; |
||
| 343 | options = options || {} ; |
||
| 344 | |||
| 345 | // if "options" is not handlebars options, merge it onto locals | ||
| 346 | if ( !utils.isOptions(options) ) { |
||
| 347 | locals = Object.assign({}, locals, options); | ||
| 348 | } | ||
| 349 | // merge handlebars root data onto locals if specified on the hash | ||
| 350 | if ( utils.isOptions(options) && options.hash.root === true) { |
||
| 351 | locals = Object.assign({}, options.data.root, locals); | ||
| 352 | } | ||
| 353 | 10 | var context = Object.assign({}, appContext, locals, options.hash); | |
| 354 | if ( !utils.isApp(thisArg) ) { |
||
| 355 | 10 | context = Object.assign({}, thisArg, context); | |
| 356 | } | ||
| 357 | if ( utils.isApp(thisArg) && thisArg.view && thisArg.view.data ) { |
||
| 358 | context = Object.assign({}, context, thisArg.view.data); | ||
| 359 | } | ||
| 360 | 10 | return context; | |
| 361 | }; | ||
| 362 | |||
| 363 | /** | ||
| 364 | * Creates an options object from the `context`, `locals` and `options.` | ||
| 365 | * Handlebars' `options.hash` is merged onto the options, and if the context | ||
| 366 | * is created by [templates][], `this.options` will be merged onto the | ||
| 367 | * options as well. | ||
| 368 | * | ||
| 369 | * @param {Object} `context` | ||
| 370 | * @param {Object} `locals` Options or locals | ||
| 371 | * @param {Object} `options` | ||
| 372 | * @return {Boolean} | ||
| 373 | * @api public | ||
| 374 | */ | ||
| 375 | |||
| 376 | 1 | utils.options = function (thisArg, locals, options) { | |
| 377 | if ( utils.isOptions(thisArg) ) { |
||
| 378 | return utils.options({}, locals, thisArg); | ||
| 379 | } | ||
| 380 | if ( utils.isOptions(locals) ) { |
||
| 381 | return utils.options(thisArg, options, locals); | ||
| 382 | } | ||
| 383 | options = options || {} ; |
||
| 384 | if ( !utils.isOptions(options) ) { |
||
| 385 | locals = Object.assign({}, locals, options); | ||
| 386 | } | ||
| 387 | 3 | var opts = Object.assign({}, locals, options.hash); | |
| 388 | if ( utils.isObject(thisArg) ) { |
||
| 389 | 3 | opts = Object.assign({}, thisArg.options, opts); | |
| 390 | } | ||
| 391 | if ( opts[options.name] ) { |
||
| 392 | opts = Object.assign({}, opts[options.name], opts); | ||
| 393 | } | ||
| 394 | 3 | return opts; | |
| 395 | }; | ||
| 396 | |||
| 397 | /** | ||
| 398 | * Returns true if an `app` propery is on the context, which means | ||
| 399 | * the context was created by [assemble][], [templates][], [verb][], | ||
| 400 | * or any other library that follows this convention. | ||
| 401 | * | ||
| 402 | * ```js | ||
| 403 | * Handlebars.registerHelper('example', function(val, options) { | ||
| 404 | * var context = options.hash; | ||
| 405 | * if (utils.isApp(this)) { | ||
| 406 | * context = Object.assign({}, this.context, context); | ||
| 407 | * } | ||
| 408 | * // do stuff | ||
| 409 | * }); | ||
| 410 | * ``` | ||
| 411 | * @param {any} `value` | ||
| 412 | * @return {Boolean} | ||
| 413 | * @api public | ||
| 414 | */ | ||
| 415 | |||
| 416 | 1 | utils.isApp = function (thisArg) { | |
| 417 | return utils.isObject(thisArg) |
||
| 418 | && utils.isObject(thisArg.options) |
||
| 419 | && utils.isObject(thisArg.app) ; |
||
| 420 | }; | ||
| 421 | |||
| 422 | /** | ||
| 423 | * Expose `utils` | ||
| 424 | */ | ||
| 425 | |||
| 426 | 1 | module.exports = utils; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /*! | ||
| 2 | * COPY of https://raw.githubusercontent.com/jonschlinkert/arr-filter/1.1.1/index.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 'use strict'; | ||
| 6 | |||
| 7 | 1 | const iterator = require('./makeIterator'); | |
| 8 | 1 | const { ValidationError } = require('../../../../lib/errors'); | |
| 9 | |||
| 10 | 1 | module.exports = function filter(arr, cb, thisArg) { | |
| 11 | if ( !arr ) { |
||
| 12 | return []; | ||
| 13 | } | ||
| 14 | |||
| 15 | 8 | if (typeof cb !== 'function') { | |
| 16 | 1 | throw new ValidationError('arr-filter expects a callback function.'); | |
| 17 | } | ||
| 18 | |||
| 19 | 7 | cb = iterator(cb, thisArg); | |
| 20 | 7 | var len = arr.length; | |
| 21 | 7 | var res = arr.slice(); | |
| 22 | 7 | var i = 0; | |
| 23 | |||
| 24 | 7 | while (len--) { | |
| 25 | 42 | if (!cb(arr[len], i++)) { | |
| 26 | 33 | res.splice(len, 1); | |
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | 7 | return res; | |
| 31 | }; | ||
| 32 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COPY of https://raw.githubusercontent.com/jonschlinkert/arr-flatten/1.1.0/index.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 'use strict'; | ||
| 6 | |||
| 7 | 1 | module.exports = function (arr) { | |
| 8 | return flat(arr, []); | ||
| 9 | }; | ||
| 10 | |||
| 11 | function flat(arr, res) { | ||
| 12 | 112 | var i = 0, cur; | |
| 13 | 112 | var len = arr.length; | |
| 14 | 112 | for (; i < len; i++) { | |
| 15 | 239 | cur = arr[i]; | |
| 16 | 239 | Array.isArray(cur) ? flat(cur, res) : res.push(cur); | |
| 17 | } | ||
| 18 | 112 | return res; | |
| 19 | } |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /* | ||
| 4 | |||
| 5 | Copy of https://github.com/jonschlinkert/array-sort/blob/0.1.2/index.js | ||
| 6 | |||
| 7 | */ | ||
| 8 | 1 | const kindOf = require('./kindOf'); | |
| 9 | 1 | const { getValue } = require('../../../lib/common'); | |
| 10 | 1 | const { ValidationError } = require('../../../../lib/errors'); | |
| 11 | |||
| 12 | /** | ||
| 13 | * Sort an array of objects by one or more properties. | ||
| 14 | * | ||
| 15 | * @param {Array} `arr` The Array to sort. | ||
| 16 | * @param {String|Array|Function} `props` One or more object paths or comparison functions. | ||
| 17 | * @param {Object} `opts` Pass `{ reverse: true }` to reverse the sort order. | ||
| 18 | * @return {Array} Returns a sorted array. | ||
| 19 | * @api public | ||
| 20 | */ | ||
| 21 | |||
| 22 | function arraySort(arr, props, opts) { | ||
| 23 | 16 | if (!arr) { | |
| 24 | 3 | return []; | |
| 25 | } | ||
| 26 | |||
| 27 | 13 | if (!Array.isArray(arr)) { | |
| 28 | 1 | throw new ValidationError('array-sort expects an array.'); | |
| 29 | } | ||
| 30 | |||
| 31 | 12 | if (arguments.length === 1) { | |
| 32 | 1 | return arr.sort(); | |
| 33 | } | ||
| 34 | |||
| 35 | 11 | var args = flatten([].slice.call(arguments, 1)); | |
| 36 | |||
| 37 | // if the last argument appears to be a plain object, | ||
| 38 | // it's not a valid `compare` arg, so it must be options. | ||
| 39 | 11 | if (kindOf(args[args.length - 1]) === 'object') { | |
| 40 | 1 | opts = args.pop(); | |
| 41 | } | ||
| 42 | 11 | return arr.sort(sortBy(args, opts)); | |
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Iterate over each comparison property or function until `1` or `-1` | ||
| 47 | * is returned. | ||
| 48 | * | ||
| 49 | * @param {String|Array|Function} `props` One or more object paths or comparison functions. | ||
| 50 | * @param {Object} `opts` Pass `{ reverse: true }` to reverse the sort order. | ||
| 51 | * @return {Array} | ||
| 52 | */ | ||
| 53 | |||
| 54 | function sortBy(props, opts) { | ||
| 55 | 11 | opts = opts || {}; | |
| 56 | |||
| 57 | 11 | return function compareFn(a, b) { | |
| 58 | var len = props.length, i = -1; | ||
| 59 | 138 | var result; | |
| 60 | |||
| 61 | 138 | while (++i < len) { | |
| 62 | 235 | result = compare(props[i], a, b); | |
| 63 | 235 | if (result !== 0) { | |
| 64 | 134 | break; | |
| 65 | } | ||
| 66 | } | ||
| 67 | 138 | if (opts.reverse === true) { | |
| 68 | 35 | return result * -1; | |
| 69 | } | ||
| 70 | 103 | return result; | |
| 71 | }; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Compare `a` to `b`. If an object `prop` is passed, then | ||
| 76 | * `a[prop]` is compared to `b[prop]` | ||
| 77 | */ | ||
| 78 | |||
| 79 | function compare(prop, a, b) { | ||
| 80 | 449 | if (typeof prop === 'function') { | |
| 81 | // expose `compare` to custom function | ||
| 82 | 72 | return prop(a, b, compare.bind(null, null)); | |
| 83 | } | ||
| 84 | // compare object values | ||
| 85 | if (prop && typeof a === 'object' && typeof b === 'object') { |
||
| 86 | 163 | return compare(null, getValue(a, prop), getValue(b, prop)); | |
| 87 | } | ||
| 88 | 214 | return defaultCompare(a, b); | |
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Default compare function used as a fallback | ||
| 93 | * for sorting. | ||
| 94 | */ | ||
| 95 | |||
| 96 | function defaultCompare(a, b) { | ||
| 97 | 214 | return a < b ? -1 : (a > b ? 1 : 0); | |
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * Flatten the given array. | ||
| 102 | */ | ||
| 103 | |||
| 104 | function flatten(arr) { | ||
| 105 | 11 | return [].concat.apply([], arr); | |
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Expose `arraySort` | ||
| 110 | */ | ||
| 111 | |||
| 112 | 1 | module.exports = arraySort; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * | ||
| 3 | * https://github.com/jonschlinkert/create-frame/blob/master/index.js | ||
| 4 | * https://github.com/jonschlinkert/define-property/blob/master/index.js | ||
| 5 | */ | ||
| 6 | |||
| 7 | |||
| 8 | 1 | const { ValidationError } = require('../../../../lib/errors'); | |
| 9 | |||
| 10 | function extend(obj /* , ...source */) { | ||
| 11 | 50 | for (var i = 1; i < arguments.length; i++) { | |
| 12 | 50 | for (var key in arguments[i]) { | |
| 13 | if ( Object.prototype.hasOwnProperty.call(arguments[i], key) ) { |
||
| 14 | 94 | obj[key] = arguments[i][key]; | |
| 15 | } | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | 50 | return obj; | |
| 20 | } | ||
| 21 | |||
| 22 | function defineProperty(obj, prop, val) { | ||
| 23 | if ( typeof obj !== 'object' && typeof obj !== 'function' ) { |
||
| 24 | throw new ValidationError('expected an object or function.'); | ||
| 25 | } | ||
| 26 | |||
| 27 | if ( typeof prop !== 'string' ) { |
||
| 28 | throw new ValidationError('expected `prop` to be a string.'); | ||
| 29 | } | ||
| 30 | |||
| 31 | |||
| 32 | 23 | return Object.defineProperty(obj, prop, { | |
| 33 | configurable: true, | ||
| 34 | enumerable: false, | ||
| 35 | writable: true, | ||
| 36 | value: val | ||
| 37 | }); | ||
| 38 | }; | ||
| 39 | |||
| 40 | 1 | module.exports = function createFrame(data) { | |
| 41 | if (typeof data !== 'object') { | ||
| 42 | 1 | throw new ValidationError('createFrame expects data to be an object'); | |
| 43 | } | ||
| 44 | |||
| 45 | 23 | var frame = extend({}, data); | |
| 46 | 23 | frame._parent = data; | |
| 47 | |||
| 48 | 23 | defineProperty(frame, 'extend', function (data) { | |
| 49 | extend(this, data); | ||
| 50 | }); | ||
| 51 | |||
| 52 | 23 | if (arguments.length > 1) { | |
| 53 | 18 | var args = [].slice.call(arguments, 1); | |
| 54 | 18 | var len = args.length, i = -1; | |
| 55 | 18 | while (++i < len) { | |
| 56 | 20 | frame.extend(args[i] || {}); | |
| 57 | } | ||
| 58 | } | ||
| 59 | 23 | return frame; | |
| 60 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * Recursively compare objects | ||
| 3 | */ | ||
| 4 | 1 | const typeOf = require("./kindOf"); | |
| 5 | |||
| 6 | function deepMatches(val, value) { | ||
| 7 | 38 | if (typeOf(val) === "object") { | |
| 8 | if ( Array.isArray(val) && Array.isArray(value) ) { |
||
| 9 | return matchArray(val, value); | ||
| 10 | } else { | ||
| 11 | 19 | return matchObject(val, value); | |
| 12 | } | ||
| 13 | } else { | ||
| 14 | 19 | return isMatch(val, value); | |
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | function containsMatch(array, value) { | ||
| 19 | var len = array.length; | ||
| 20 | var i = -1; | ||
| 21 | |||
| 22 | while (++i < len) { | ||
| 23 | if (deepMatches(array[i], value)) { | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | return false; | ||
| 28 | } | ||
| 29 | |||
| 30 | function matchArray(arr, value) { | ||
| 31 | var len = value.length; | ||
| 32 | var i = -1; | ||
| 33 | |||
| 34 | while (++i < len) { | ||
| 35 | if (!containsMatch(arr, value[i])) { | ||
| 36 | return false; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | return true; | ||
| 40 | } | ||
| 41 | |||
| 42 | function matchObject(obj, value) { | ||
| 43 | 19 | for (var key in value) { | |
| 44 | if ( Object.hasOwnProperty.call(value, key) ) { |
||
| 45 | 21 | if (deepMatches(obj[key], value[key]) === false) { | |
| 46 | 14 | return false; | |
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | 5 | return true; | |
| 51 | } | ||
| 52 | |||
| 53 | function isMatch(target, val) { | ||
| 54 | 19 | return target === val; | |
| 55 | } | ||
| 56 | |||
| 57 | 1 | module.exports = deepMatches; | |
| 58 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COPY of https://github.com/jonschlinkert/for-in/blob/1.0.1/index.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 'use strict'; | ||
| 6 | |||
| 7 | 1 | module.exports = function forIn(obj, fn, thisArg) { | |
| 8 | for (var key in obj) { | ||
| 9 | 211 | if (fn.call(thisArg, obj[key], key, obj) === false) { | |
| 10 | 1 | break; | |
| 11 | } | ||
| 12 | } | ||
| 13 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | |||
| 2 | /* | ||
| 3 | * COPY of https://github.com/jonschlinkert/for-own/tree/0.1.5/index.js | ||
| 4 | */ | ||
| 5 | |||
| 6 | 'use strict'; | ||
| 7 | |||
| 8 | 1 | var forIn = require('./forIn'); | |
| 9 | 1 | var hasOwn = Object.prototype.hasOwnProperty; | |
| 10 | |||
| 11 | 1 | module.exports = function forOwn(obj, fn, thisArg) { | |
| 12 | forIn(obj, function(val, key) { | ||
| 13 | if ( hasOwn.call(obj, key) ) { |
||
| 14 | 3 | return fn.call(thisArg, obj[key], key, obj); | |
| 15 | } | ||
| 16 | }); | ||
| 17 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COPY of https://github.com/jonschlinkert/index-of/blob/0.2.0/index.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | |||
| 6 | 'use strict'; | ||
| 7 | |||
| 8 | 1 | module.exports = function indexOf(arr, ele, start) { | |
| 9 | start = start || 0; | ||
| 10 | 19 | var idx = -1; | |
| 11 | |||
| 12 | 20 | if (!arr) {return idx;} | |
| 13 | 18 | var len = arr.length; | |
| 14 | var i = start < 0 |
||
| 15 | ? ( len + start ) |
||
| 16 | : start; | ||
| 17 | |||
| 18 | 18 | if (i >= arr.length) { | |
| 19 | 1 | return -1; | |
| 20 | } | ||
| 21 | |||
| 22 | 17 | while (i < len) { | |
| 23 | 49 | if (arr[i] === ele) { | |
| 24 | 8 | return i; | |
| 25 | } | ||
| 26 | 41 | i++; | |
| 27 | } | ||
| 28 | |||
| 29 | 9 | return -1; | |
| 30 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * Copy of https://raw.githubusercontent.com/i-voted-for-trump/is-even/0.1.2/index.js | ||
| 5 | */ | ||
| 6 | |||
| 7 | 1 | var isOdd = require('./isOdd'); | |
| 8 | |||
| 9 | 1 | module.exports = function isEven(i) { | |
| 10 | return !isOdd(i); | ||
| 11 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const isPlainObject = require('./isPlainObject'); | |
| 2 | |||
| 3 | 1 | module.exports = function isExtendable(val) { | |
| 4 | return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); | ||
| 5 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | module.exports = function(num) { | |
| 4 | if (typeof num === 'number') { | ||
| 5 | 155 | return num - num === 0; | |
| 6 | } | ||
| 7 | 71 | if (typeof num === 'string' && num.trim() !== '') { | |
| 8 | return Number.isFinite ? Number.isFinite(+num) : isFinite(+num) ; |
||
| 9 | } | ||
| 10 | 33 | return false; | |
| 11 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | Copy form https://github.com/i-voted-for-trump/is-odd/blob/0.1.2/index.js | ||
| 3 | */ | ||
| 4 | 'use strict'; | ||
| 5 | |||
| 6 | 1 | const { ValidationError } = require('../../../../lib/errors'); | |
| 7 | 1 | const isNumber = require('./isNumber'); | |
| 8 | |||
| 9 | 1 | module.exports = function isOdd(i) { | |
| 10 | if (!isNumber(i)) { | ||
| 11 | 3 | throw new ValidationError('is-odd expects a number.'); | |
| 12 | } | ||
| 13 | 28 | if (Number(i) !== Math.floor(i)) { | |
| 14 | 2 | throw new ValidationError('is-odd expects an integer.'); | |
| 15 | } | ||
| 16 | 26 | return !!(~~i & 1); | |
| 17 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | function isObject(o) { | ||
| 2 | 608 | return Object.prototype.toString.call(o) === '[object Object]'; | |
| 3 | } | ||
| 4 | |||
| 5 | function isPlainObject(o) { | ||
| 6 | 450 | var ctor,prot; | |
| 7 | |||
| 8 | 741 | if (isObject(o) === false) {return false;} | |
| 9 | |||
| 10 | // If has modified constructor | ||
| 11 | 159 | ctor = o.constructor; | |
| 12 | 160 | if (ctor === undefined) {return true;} | |
| 13 | |||
| 14 | // If has modified prototype | ||
| 15 | 158 | prot = ctor.prototype; | |
| 16 | if ( isObject(prot) === false ) {return false;} |
||
| 17 | |||
| 18 | // If constructor does not have an Object-specific method | ||
| 19 | 158 | if (Object.hasOwnProperty.call(prot, 'isPrototypeOf') === false) { | |
| 20 | 1 | return false; | |
| 21 | } | ||
| 22 | |||
| 23 | // Most likely a plain Object | ||
| 24 | 157 | return true; | |
| 25 | }; | ||
| 26 | |||
| 27 | 1 | module.exports = isPlainObject; | |
| 28 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | var toString = Object.prototype.toString; | |
| 2 | |||
| 3 | /** | ||
| 4 | * | ||
| 5 | * COPY of https://raw.githubusercontent.com/jonschlinkert/kind-of/2.0.0/index.js | ||
| 6 | * | ||
| 7 | * | ||
| 8 | * Get the native `typeof` a value. | ||
| 9 | * | ||
| 10 | * @param {*} `val` | ||
| 11 | * @return {*} Native javascript type | ||
| 12 | */ | ||
| 13 | |||
| 14 | 1 | module.exports = function kindOf(val) { | |
| 15 | // primitivies | ||
| 16 | if (typeof val === 'undefined') { | ||
| 17 | 19 | return 'undefined'; | |
| 18 | } | ||
| 19 | 89 | if (val === null) { | |
| 20 | 2 | return 'null'; | |
| 21 | } | ||
| 22 | 87 | if (val === true || val === false || val instanceof Boolean) { | |
| 23 | 3 | return 'boolean'; | |
| 24 | } | ||
| 25 | 84 | if (typeof val === 'string' || val instanceof String) { | |
| 26 | 9 | return 'string' | |
| 27 | } | ||
| 28 | 75 | if (typeof val === 'number' || val instanceof Number) { | |
| 29 | 12 | return 'number' | |
| 30 | } | ||
| 31 | |||
| 32 | // functions | ||
| 33 | 63 | if (typeof val === 'function' || val instanceof Function) { | |
| 34 | 23 | return 'function' | |
| 35 | } | ||
| 36 | |||
| 37 | // array | ||
| 38 | if ( typeof Array.isArray !== 'undefined' && Array.isArray(val)) { |
||
| 39 | 3 | return 'array'; | |
| 40 | } | ||
| 41 | |||
| 42 | // check for instances of RegExp and Date before calling `toString` | ||
| 43 | 37 | if (val instanceof RegExp) { | |
| 44 | 4 | return 'regexp'; | |
| 45 | } | ||
| 46 | 33 | if (val instanceof Date) { | |
| 47 | 1 | return 'date'; | |
| 48 | } | ||
| 49 | |||
| 50 | // other objects | ||
| 51 | 32 | var type = toString.call(val); | |
| 52 | |||
| 53 | if ( type === '[object RegExp]' ) { |
||
| 54 | return 'regexp'; | ||
| 55 | } | ||
| 56 | if ( type === '[object Date]' ) { |
||
| 57 | return 'date'; | ||
| 58 | } | ||
| 59 | 32 | if (type === '[object Arguments]') { | |
| 60 | 1 | return 'arguments'; | |
| 61 | } | ||
| 62 | |||
| 63 | // buffer | ||
| 64 | if ( typeof Buffer !== 'undefined' && Buffer.isBuffer(val)) { |
||
| 65 | 1 | return 'buffer'; | |
| 66 | } | ||
| 67 | |||
| 68 | // es6: Map, WeakMap, Set, WeakSet | ||
| 69 | 30 | if (type === '[object Set]') { | |
| 70 | 1 | return 'set'; | |
| 71 | } | ||
| 72 | 29 | if (type === '[object WeakSet]') { | |
| 73 | 1 | return 'weakset'; | |
| 74 | } | ||
| 75 | 28 | if (type === '[object Map]') { | |
| 76 | 1 | return 'map'; | |
| 77 | } | ||
| 78 | 27 | if (type === '[object WeakMap]') { | |
| 79 | 1 | return 'weakmap'; | |
| 80 | } | ||
| 81 | 26 | if (type === '[object Symbol]') { | |
| 82 | 2 | return 'symbol'; | |
| 83 | } | ||
| 84 | |||
| 85 | // must be a plain object | ||
| 86 | 24 | return 'object'; | |
| 87 | }; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /*! | ||
| 2 | * COPY of https://raw.githubusercontent.com/jonschlinkert/make-iterator/0.3.0/index.js | ||
| 3 | */ | ||
| 4 | 1 | const typeOf = require('./kindOf'); | |
| 5 | 1 | const deepMatches = require('./deepMatches'); | |
| 6 | |||
| 7 | 1 | module.exports = function makeIterator(target, thisArg) { | |
| 8 | switch (typeOf(target)) { | ||
| 9 | case 'undefined': | ||
| 10 | case 'null': | ||
| 11 | 3 | return noop; | |
| 12 | case 'function': | ||
| 13 | // function is the first to improve perf (most common case) | ||
| 14 | // also avoid using `Function#call` if not needed, which boosts | ||
| 15 | // perf a lot in some cases | ||
| 16 | 11 | return (typeof thisArg !== 'undefined') ? function(val, i, arr) { | |
| 17 | return target.call(thisArg, val, i, arr); | ||
| 18 | } : target; | ||
| 19 | case 'object': | ||
| 20 | 1 | return function(val) { | |
| 21 | return deepMatches(val, target); | ||
| 22 | }; | ||
| 23 | case 'regexp': | ||
| 24 | 2 | return function(str) { | |
| 25 | return target.test(str); | ||
| 26 | }; | ||
| 27 | case 'string': | ||
| 28 | case 'number': | ||
| 29 | default: { | ||
| 30 | 2 | return prop(target); | |
| 31 | } | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | |||
| 35 | |||
| 36 | function prop(name) { | ||
| 37 | 2 | return function(obj) { | |
| 38 | return obj[name]; | ||
| 39 | }; | ||
| 40 | } | ||
| 41 | |||
| 42 | function noop(val) { | ||
| 43 | 6 | return val; | |
| 44 | } |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const merge = require('./mixinDeep'); | |
| 2 | 1 | const {Remarkable} = require('remarkable'); | |
| 3 | 1 | const {linkify} = require('remarkable/linkify'); | |
| 4 | |||
| 5 | 1 | module.exports = function markdown(config) { | |
| 6 | |||
| 7 | if (typeof config === 'string') { | ||
| 8 | 5 | return helper.apply(this, arguments); | |
| 9 | } | ||
| 10 | |||
| 11 | 5 | config = config || {}; | |
| 12 | if ( config.fn || config.hash || arguments.length > 1) { |
||
| 13 | return helper.apply(this, arguments); | ||
| 14 | } | ||
| 15 | |||
| 16 | function helper(context, options) { | ||
| 17 | |||
| 18 | 12 | if (typeof context === 'string') { | |
| 19 | 5 | const opts = merge({}, config, options); | |
| 20 | 5 | const md = buildRemarkable(opts); | |
| 21 | 5 | return md.render(context); | |
| 22 | } | ||
| 23 | |||
| 24 | if ( typeof context === 'object' && typeof context.fn === 'function') { |
||
| 25 | 7 | options = context; | |
| 26 | 7 | context = {}; | |
| 27 | } | ||
| 28 | |||
| 29 | 7 | options = merge({html: true, breaks: true}, config, options); | |
| 30 | 7 | options = merge({}, options, options.markdown, options.hash); | |
| 31 | if ( Object.hasOwnProperty.call(options, 'lang') ) { |
||
| 32 | options.langPrefix = options.lang; | ||
| 33 | } | ||
| 34 | |||
| 35 | 7 | const md = buildRemarkable(options); | |
| 36 | const ctx = merge({}, options, ( this.context || this), context); |
||
| 37 | 7 | return md.render(options.fn(ctx)); | |
| 38 | } | ||
| 39 | |||
| 40 | function buildRemarkable(options) { | ||
| 41 | 12 | const remarkable = options.linkify === true ? new Remarkable(options).use(linkify) : new Remarkable(options); | |
| 42 | 12 | delete options.linkify; | |
| 43 | 12 | return remarkable; | |
| 44 | } | ||
| 45 | |||
| 46 | 5 | return helper; | |
| 47 | }; | ||
| 48 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | var isExtendable = require('./isExtendable'); | |
| 4 | 1 | var forIn = require('./forIn'); | |
| 5 | |||
| 6 | function mixinDeep(target, objects) { | ||
| 7 | 57 | var len = arguments.length, i = 0; | |
| 8 | 57 | while (++i < len) { | |
| 9 | 103 | var obj = arguments[i]; | |
| 10 | 103 | if (isObject(obj)) { | |
| 11 | 82 | forIn(obj, copy, target); | |
| 12 | } | ||
| 13 | } | ||
| 14 | 57 | return target; | |
| 15 | } | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Copy properties from the source object to the | ||
| 19 | * target object. | ||
| 20 | * | ||
| 21 | * @param {*} `val` | ||
| 22 | * @param {String} `key` | ||
| 23 | */ | ||
| 24 | |||
| 25 | function copy(val, key) { | ||
| 26 | if ( !isValidKey(key) ) { |
||
| 27 | return; | ||
| 28 | } | ||
| 29 | |||
| 30 | 203 | var obj = this[key]; | |
| 31 | 203 | if (isObject(val) && isObject(obj)) { | |
| 32 | 13 | mixinDeep(obj, val); | |
| 33 | } else { | ||
| 34 | 190 | this[key] = val; | |
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Returns true if `val` is an object or function. | ||
| 40 | * | ||
| 41 | * @param {any} val | ||
| 42 | * @return {Boolean} | ||
| 43 | */ | ||
| 44 | |||
| 45 | function isObject(val) { | ||
| 46 | 424 | return isExtendable(val) && !Array.isArray(val); | |
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Returns true if `key` is a valid key to use when extending objects. | ||
| 51 | * | ||
| 52 | * @param {String} `key` | ||
| 53 | * @return {Boolean} | ||
| 54 | */ | ||
| 55 | |||
| 56 | function isValidKey(key) { | ||
| 57 | return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; |
||
| 58 | }; | ||
| 59 | |||
| 60 | /** | ||
| 61 | * Expose `mixinDeep` | ||
| 62 | */ | ||
| 63 | |||
| 64 | 1 | module.exports = mixinDeep; |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const STATE_OUTPUT = 0, | |
| 2 | STATE_HTML = 1, | ||
| 3 | STATE_PRE_COMMENT = 2, | ||
| 4 | STATE_COMMENT = 3, | ||
| 5 | WHITESPACE = /\s/, | ||
| 6 | ALLOWED_TAGS_REGEX = /<(\w*)>/g; | ||
| 7 | |||
| 8 | function striptags(html, allowableTags) { | ||
| 9 | var html = html || '' , |
||
| 10 | state = STATE_OUTPUT, | ||
| 11 | depth = 0, | ||
| 12 | output = '', | ||
| 13 | tagBuffer = '', | ||
| 14 | inQuote = false, | ||
| 15 | i, length, c; | ||
| 16 | |||
| 17 | 19 | if (typeof allowableTags === 'string') { | |
| 18 | // Parse the string into an array of tags | ||
| 19 | 6 | allowableTags = parseAllowableTags(allowableTags); | |
| 20 | 13 | } else if (!Array.isArray(allowableTags)) { | |
| 21 | // If it is not an array, explicitly set to null | ||
| 22 | 10 | allowableTags = null; | |
| 23 | } | ||
| 24 | |||
| 25 | 19 | for (i = 0, length = html.length; i < length; i++) { | |
| 26 | 634 | c = html[i]; | |
| 27 | |||
| 28 | 634 | switch (c) { | |
| 29 | case '<': { | ||
| 30 | // ignore '<' if inside a quote | ||
| 31 | 43 | if (inQuote) { | |
| 32 | 1 | break; | |
| 33 | } | ||
| 34 | |||
| 35 | // '<' followed by a space is not a valid tag, continue | ||
| 36 | 42 | if (html[i + 1] == ' ') { | |
| 37 | 1 | consumeCharacter(c); | |
| 38 | 1 | break; | |
| 39 | } | ||
| 40 | |||
| 41 | // change to STATE_HTML | ||
| 42 | 41 | if (state == STATE_OUTPUT) { | |
| 43 | 38 | state = STATE_HTML; | |
| 44 | |||
| 45 | 38 | consumeCharacter(c); | |
| 46 | 38 | break; | |
| 47 | } | ||
| 48 | |||
| 49 | // ignore additional '<' characters when inside a tag | ||
| 50 | 3 | if (state == STATE_HTML) { | |
| 51 | 1 | depth++; | |
| 52 | 1 | break; | |
| 53 | } | ||
| 54 | |||
| 55 | 2 | consumeCharacter(c); | |
| 56 | 2 | break; | |
| 57 | } | ||
| 58 | |||
| 59 | case '>': { | ||
| 60 | // something like this is happening: '<<>>' | ||
| 61 | 43 | if (depth) { | |
| 62 | 1 | depth--; | |
| 63 | 1 | break; | |
| 64 | } | ||
| 65 | |||
| 66 | // ignore '>' if inside a quote | ||
| 67 | 42 | if (inQuote) { | |
| 68 | 1 | break; | |
| 69 | } | ||
| 70 | |||
| 71 | // an HTML tag was closed | ||
| 72 | 41 | if (state == STATE_HTML) { | |
| 73 | 35 | inQuote = state = 0; | |
| 74 | |||
| 75 | 35 | if (allowableTags) { | |
| 76 | 26 | tagBuffer += '>'; | |
| 77 | 26 | flushTagBuffer(); | |
| 78 | } | ||
| 79 | |||
| 80 | 35 | break; | |
| 81 | } | ||
| 82 | |||
| 83 | // '<!' met its ending '>' | ||
| 84 | 6 | if (state == STATE_PRE_COMMENT) { | |
| 85 | 1 | inQuote = state = 0; | |
| 86 | 1 | tagBuffer = ''; | |
| 87 | 1 | break; | |
| 88 | } | ||
| 89 | |||
| 90 | // if last two characters were '--', then end comment | ||
| 91 | 5 | if (state == STATE_COMMENT && | |
| 92 | html[i - 1] == '-' && | ||
| 93 | html[i - 2] == '-') { | ||
| 94 | |||
| 95 | 2 | inQuote = state = 0; | |
| 96 | 2 | tagBuffer = ''; | |
| 97 | 2 | break; | |
| 98 | } | ||
| 99 | |||
| 100 | 3 | consumeCharacter(c); | |
| 101 | 3 | break; | |
| 102 | } | ||
| 103 | |||
| 104 | // catch both single and double quotes | ||
| 105 | case '"': | ||
| 106 | case '\'': { | ||
| 107 | if ( state == STATE_HTML ) { |
||
| 108 | 12 | if (inQuote == c) { | |
| 109 | // end quote found | ||
| 110 | 5 | inQuote = false; | |
| 111 | 7 | } else if (!inQuote) { | |
| 112 | // start quote only if not already in one | ||
| 113 | 5 | inQuote = c; | |
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | 12 | consumeCharacter(c); | |
| 118 | 12 | break; | |
| 119 | } | ||
| 120 | |||
| 121 | case '!': { | ||
| 122 | 5 | if (state == STATE_HTML && | |
| 123 | html[i - 1] == '<') { | ||
| 124 | |||
| 125 | // looks like we might be starting a comment | ||
| 126 | 4 | state = STATE_PRE_COMMENT; | |
| 127 | 4 | break; | |
| 128 | } | ||
| 129 | |||
| 130 | 1 | consumeCharacter(c); | |
| 131 | 1 | break; | |
| 132 | } | ||
| 133 | |||
| 134 | case '-': { | ||
| 135 | // if the previous two characters were '!-', this is a comment | ||
| 136 | 8 | if (state == STATE_PRE_COMMENT && | |
| 137 | html[i - 1] == '-' && | ||
| 138 | html[i - 2] == '!') { | ||
| 139 | |||
| 140 | 2 | state = STATE_COMMENT; | |
| 141 | 2 | break; | |
| 142 | } | ||
| 143 | |||
| 144 | 6 | consumeCharacter(c); | |
| 145 | 6 | break; | |
| 146 | } | ||
| 147 | |||
| 148 | case 'E': | ||
| 149 | case 'e': { | ||
| 150 | // check for DOCTYPE, because it looks like a comment and isn't | ||
| 151 | 35 | if (state == STATE_PRE_COMMENT && | |
| 152 | html.substr(i - 6, 7).toLowerCase() == 'doctype') { | ||
| 153 | |||
| 154 | 1 | state = STATE_HTML; | |
| 155 | 1 | break; | |
| 156 | } | ||
| 157 | |||
| 158 | 34 | consumeCharacter(c); | |
| 159 | 34 | break; | |
| 160 | } | ||
| 161 | |||
| 162 | default: { | ||
| 163 | 488 | consumeCharacter(c); | |
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | function consumeCharacter(c) { | ||
| 169 | 585 | if (state == STATE_OUTPUT) { | |
| 170 | 249 | output += c; | |
| 171 | 336 | } else if (allowableTags && state == STATE_HTML) { | |
| 172 | 210 | tagBuffer += c; | |
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | function flushTagBuffer() { | ||
| 177 | 26 | var normalized = '', | |
| 178 | nonWhitespaceSeen = false, | ||
| 179 | i, length, c; | ||
| 180 | |||
| 181 | 26 | normalizeTagBuffer: | |
| 182 | for (i = 0, length = tagBuffer.length; i < length ; i++) { |
||
| 183 | 169 | c = tagBuffer[i].toLowerCase(); | |
| 184 | |||
| 185 | 169 | switch (c) { | |
| 186 | case '<': { | ||
| 187 | 26 | break; | |
| 188 | } | ||
| 189 | |||
| 190 | case '>': { | ||
| 191 | 22 | break normalizeTagBuffer; | |
| 192 | } | ||
| 193 | |||
| 194 | case '/': { | ||
| 195 | 13 | nonWhitespaceSeen = true; | |
| 196 | 13 | break; | |
| 197 | } | ||
| 198 | |||
| 199 | default: { | ||
| 200 | 108 | if (!c.match(WHITESPACE)) { | |
| 201 | 104 | nonWhitespaceSeen = true; | |
| 202 | 104 | normalized += c; | |
| 203 | } else if ( nonWhitespaceSeen ) { |
||
| 204 | 4 | break normalizeTagBuffer; | |
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | 26 | if (allowableTags.indexOf(normalized) !== -1) { | |
| 211 | 16 | output += tagBuffer; | |
| 212 | } | ||
| 213 | |||
| 214 | 26 | tagBuffer = ''; | |
| 215 | } | ||
| 216 | |||
| 217 | 19 | return output; | |
| 218 | } | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Return an array containing tags that are allowed to pass through the | ||
| 222 | * algorithm. | ||
| 223 | * | ||
| 224 | * @param string allowableTags A string of tags to allow (e.g. "<b><strong>"). | ||
| 225 | * @return array|null An array of allowed tags or null if none. | ||
| 226 | */ | ||
| 227 | function parseAllowableTags(allowableTags) { | ||
| 228 | 6 | var tagsArray = [], | |
| 229 | match; | ||
| 230 | |||
| 231 | 6 | while ((match = ALLOWED_TAGS_REGEX.exec(allowableTags)) !== null) { | |
| 232 | 6 | tagsArray.push(match[1]); | |
| 233 | } | ||
| 234 | |||
| 235 | return tagsArray.length !== 0 ? tagsArray : null ; |
||
| 236 | } | ||
| 237 | |||
| 238 | 1 | module.exports = striptags; | |
| 239 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @deprecate Use {{#for start end (context)}}...{{/for}} | ||
| 5 | */ | ||
| 6 | 1 | const factory = () => { | |
| 7 | return function(start, end) { | ||
| 8 | const options = arguments[arguments.length - 1]; |
||
| 9 | var out = ''; | ||
| 10 | var i = start; | ||
| 11 | |||
| 12 | for (i; i <= end; i++) { | ||
| 13 | out = out + options.fn(i); | ||
| 14 | } | ||
| 15 | |||
| 16 | return out + ''; | ||
| 17 | }; | ||
| 18 | }; | ||
| 19 | |||
| 20 | 1 | module.exports = [{ | |
| 21 | name: 'enumerate', | ||
| 22 | factory: factory, | ||
| 23 | }]; | ||
| 24 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @deprecate Use {{#if val1 '==' val2}}...{{/if}} | ||
| 5 | */ | ||
| 6 | 1 | const factory = () => { | |
| 7 | return function(val1, val2) { | ||
| 8 | const options = arguments[arguments.length - 1]; | ||
| 9 | |||
| 10 | 4 | if (val1 != val2) { | |
| 11 | 2 | return ''; | |
| 12 | } | ||
| 13 | |||
| 14 | 2 | return options.fn(); | |
| 15 | }; | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | module.exports = [{ | |
| 19 | name: 'equals', | ||
| 20 | factory: factory, | ||
| 21 | }]; | ||
| 22 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * @deprecate Use lang + concat | ||
| 5 | */ | ||
| 6 | 1 | const factory = () => { | |
| 7 | return function(index) { | ||
| 8 | switch (index) { | ||
| 9 | case 1: | ||
| 10 | 1 | return 'Jan'; | |
| 11 | case 2: | ||
| 12 | 1 | return 'Feb'; | |
| 13 | case 3: | ||
| 14 | 1 | return 'Mar'; | |
| 15 | case 4: | ||
| 16 | 1 | return 'Apr'; | |
| 17 | case 5: | ||
| 18 | 1 | return 'May'; | |
| 19 | case 6: | ||
| 20 | 1 | return 'Jun'; | |
| 21 | case 7: | ||
| 22 | 1 | return 'Jul'; | |
| 23 | case 8: | ||
| 24 | 1 | return 'Aug'; | |
| 25 | case 9: | ||
| 26 | 1 | return 'Sep'; | |
| 27 | case 10: | ||
| 28 | 1 | return 'Oct'; | |
| 29 | case 11: | ||
| 30 | 1 | return 'Nov'; | |
| 31 | case 12: | ||
| 32 | 1 | return 'Dec'; | |
| 33 | } | ||
| 34 | |||
| 35 | return ''; | ||
| 36 | }; | ||
| 37 | }; | ||
| 38 | |||
| 39 | 1 | module.exports = [{ | |
| 40 | name: 'getShortMonth', | ||
| 41 | factory: factory, | ||
| 42 | }]; | ||
| 43 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | |||
| 4 | 1 | const factory = () => { | |
| 5 | /** | ||
| 6 | * @deprecate | ||
| 7 | */ | ||
| 8 | return function(...args) { | ||
| 9 | args.pop(); |
||
| 10 | const target = args.shift(); | ||
| 11 | const toReturn = {}; | ||
| 12 | const paths = args[0]; | ||
| 13 | if (paths && Array.isArray(paths)) { | ||
| 14 | paths.forEach((key) => { | ||
| 15 | if (Object.hasOwnProperty.call(target, key)) { |
||
| 16 | toReturn[key] = target[key]; | ||
| 17 | } |
||
| 18 | }) | ||
| 19 | } | ||
| 20 | |||
| 21 | return toReturn; | ||
| 22 | }; | ||
| 23 | }; | ||
| 24 | |||
| 25 | 1 | module.exports = [{ | |
| 26 | name: 'pick', | ||
| 27 | factory: factory, | ||
| 28 | }]; | ||
| 29 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | // Return a function that can be used to translate paths to cdn paths | ||
| 4 | 1 | module.exports = globals => { | |
| 5 | /** | ||
| 6 | * Add CDN base url to the relative path | ||
| 7 | * @param {String} path Relative path | ||
| 8 | * @return {String} Url cdn | ||
| 9 | */ | ||
| 10 | return function(path) { | ||
| 11 | const siteSettings = globals.getSiteSettings(); | ||
| 12 | 57 | const themeSettings = globals.getThemeSettings(); | |
| 13 | |||
| 14 | 57 | const cdnUrl = siteSettings.cdn_url || ''; | |
| 15 | 57 | const versionId = siteSettings.theme_version_id; | |
| 16 | 57 | const editSessionId = siteSettings.theme_session_id; | |
| 17 | 57 | const cdnSettings = themeSettings.cdn; | |
| 18 | |||
| 19 | 57 | if (path instanceof globals.handlebars.SafeString) { | |
| 20 | 6 | path = path.string; | |
| 21 | } | ||
| 22 | |||
| 23 | 57 | if (!path) { | |
| 24 | 2 | return ''; | |
| 25 | } | ||
| 26 | |||
| 27 | 55 | if (path.startsWith('https://') || path.startsWith('http://') || path.startsWith('//')) { | |
| 28 | 3 | return path; | |
| 29 | } | ||
| 30 | // This block is to strip leading . and / from path | ||
| 31 | 52 | if (['.', '/'].includes(path[0])) { | |
| 32 | for (let i = 0; i < path.length ; i++) { |
||
| 33 | 102 | if (path[i] !== '.' && path[i] !== '/') { | |
| 34 | 15 | path = path.slice(i); | |
| 35 | 15 | break; | |
| 36 | } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | 52 | let colonIndex = path.indexOf(':'); | |
| 41 | |||
| 42 | 52 | if (colonIndex !== -1) { | |
| 43 | while ( colonIndex + 1 < path.length && path[colonIndex + 1] === ':') { |
||
| 44 | 1 | colonIndex++; | |
| 45 | } | ||
| 46 | 22 | const protocol = path.slice(0, colonIndex + 1); | |
| 47 | 22 | path = path.slice(colonIndex + 1); | |
| 48 | //This block is to strip leading / from path | ||
| 49 | 22 | if (path[0] === '/') { | |
| 50 | for (let i = 1; i < path.length ; i++) { |
||
| 51 | 13 | if (path[i] !== '/') { | |
| 52 | 12 | path = path.slice(i); | |
| 53 | 12 | break; | |
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | 22 | if (protocol === 'webdav:') { | |
| 59 | 3 | return [cdnUrl, 'content', path].join('/'); | |
| 60 | } | ||
| 61 | |||
| 62 | 19 | if (cdnSettings) { | |
| 63 | 17 | const endpointKey = protocol.slice(0, protocol.length - 1); | |
| 64 | 17 | if (Object.hasOwnProperty.call(cdnSettings, endpointKey)) { | |
| 65 | 13 | if (cdnUrl) { | |
| 66 | 9 | return [cdnSettings[endpointKey], path].join('/'); | |
| 67 | } | ||
| 68 | |||
| 69 | 4 | return ['/assets/cdn', endpointKey, path].join('/'); | |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | if ( path[0] !== '/' ) { |
||
| 74 | 6 | path = '/' + path; | |
| 75 | } | ||
| 76 | |||
| 77 | 6 | return path; | |
| 78 | } | ||
| 79 | |||
| 80 | 30 | if (!versionId) { | |
| 81 | if ( path[0] !== '/' ) { |
||
| 82 | 5 | path = '/' + path; | |
| 83 | } | ||
| 84 | 5 | return path; | |
| 85 | } | ||
| 86 | |||
| 87 | if ( path[0] === '/' ) { |
||
| 88 | path = path.slice(1); | ||
| 89 | } | ||
| 90 | |||
| 91 | 25 | if (path.startsWith('assets/')) { | |
| 92 | 11 | path = path.slice(7); | |
| 93 | } | ||
| 94 | |||
| 95 | if ( editSessionId ) { |
||
| 96 | return [cdnUrl, 'stencil', versionId, 'e', editSessionId, path].join('/'); | ||
| 97 | } | ||
| 98 | |||
| 99 | 25 | return [cdnUrl, 'stencil', versionId, path].join('/'); | |
| 100 | }; | ||
| 101 | }; | ||
| 102 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const url = require('url'); | |
| 3 | |||
| 4 | function isValidURL(val) { | ||
| 5 | 101 | try { | |
| 6 | 101 | return url.parse(val).hostname !== null; | |
| 7 | } catch (e) { | ||
| 8 | return false; | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Based on https://github.com/jonschlinkert/get-value/blob/2.0.6/index.js with some enhancements. | ||
| 14 | * | ||
| 15 | * - Performs "hasOwnProperty" checks for safety. | ||
| 16 | * - Now accepts Handlebars.SafeString paths. | ||
| 17 | */ | ||
| 18 | function getValue(object, path, globals) { | ||
| 19 | 494 | let parts; | |
| 20 | |||
| 21 | // for backwards compatibility | ||
| 22 | 494 | if (!path) { | |
| 23 | 3 | return object; | |
| 24 | } | ||
| 25 | |||
| 26 | // unwrap Handlebars.SafeString for compatibility with `concat` etc. | ||
| 27 | 491 | if (globals && globals.handlebars) { | |
| 28 | 40 | path = unwrapIfSafeString(globals.handlebars, path); | |
| 29 | } | ||
| 30 | |||
| 31 | // accept array or string for backwards compatibility | ||
| 32 | 491 | if (typeof path === 'string') { | |
| 33 | 481 | parts = path.split('.'); | |
| 34 | 10 | } else if (Array.isArray(path)) { | |
| 35 | 7 | parts = path; | |
| 36 | } else { | ||
| 37 | 3 | let key = String(path); | |
| 38 | 3 | return Object.keys(object).indexOf(key) !== -1 ? object[key] : undefined; | |
| 39 | } | ||
| 40 | |||
| 41 | 488 | let result = object; | |
| 42 | 488 | let prefix = ''; | |
| 43 | for (let key of parts) { | ||
| 44 | 820 | if (result === undefined || result === null) { | |
| 45 | 3 | return undefined; | |
| 46 | } | ||
| 47 | // preserve handling of trailing backslashes for backwards compatibility | ||
| 48 | 817 | if (key.slice(-1) === '\\') { | |
| 49 | 4 | prefix = prefix + key.slice(0, -1) + '.'; | |
| 50 | 4 | continue; | |
| 51 | } | ||
| 52 | 813 | key = prefix + key; | |
| 53 | 813 | if (Object.keys(result).indexOf(key) !== -1) { | |
| 54 | 729 | result = result[key]; | |
| 55 | 729 | prefix = ''; | |
| 56 | } else { | ||
| 57 | 84 | return; | |
| 58 | } | ||
| 59 | } | ||
| 60 | 401 | return result; | |
| 61 | } | ||
| 62 | |||
| 63 | function unwrapIfSafeString(handlebars, val) { | ||
| 64 | 385 | if (val instanceof handlebars.SafeString) { | |
| 65 | 12 | val = val.toString(); | |
| 66 | } | ||
| 67 | 385 | return val; | |
| 68 | } | ||
| 69 | |||
| 70 | 1 | const maximumPixelSize = 5120; | |
| 71 | |||
| 72 | /** | ||
| 73 | * Append compression=lossy query parameter to a URL if lossy is true | ||
| 74 | * @param {string} url - The URL to modify | ||
| 75 | * @param {boolean} lossy - Whether to append the lossy compression parameter | ||
| 76 | * @returns {string} - The modified URL | ||
| 77 | */ | ||
| 78 | function appendLossyParam(url, lossy) { | ||
| 79 | 183 | if (!lossy || typeof lossy !== 'boolean') { | |
| 80 | 150 | return url; | |
| 81 | } | ||
| 82 | |||
| 83 | 33 | const urlObj = new URL(url); | |
| 84 | 33 | urlObj.searchParams.set('compression', 'lossy'); | |
| 85 | 33 | return urlObj.toString(); | |
| 86 | } | ||
| 87 | |||
| 88 | 1 | module.exports = { | |
| 89 | isValidURL, | ||
| 90 | getValue, | ||
| 91 | unwrapIfSafeString, | ||
| 92 | maximumPixelSize, | ||
| 93 | appendLossyParam | ||
| 94 | }; | ||
| 95 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const URL = require('url') | |
| 4 | 1 | const utils = require('../3p/utils'); | |
| 5 | 1 | const { ValidationError } = require('../../lib/errors'); | |
| 6 | |||
| 7 | 1 | const {resourceHintAllowedTypes, addResourceHint, defaultResourceHintState} = require('../lib/resourceHints'); | |
| 8 | |||
| 9 | 1 | const fontProviders = { | |
| 10 | 'Google': { | ||
| 11 | /** | ||
| 12 | * Parser for Google fonts | ||
| 13 | * | ||
| 14 | * @param {Array} fonts - Array of fonts that might look like | ||
| 15 | * Google_Open+Sans | ||
| 16 | * Google_Open+Sans_400 | ||
| 17 | * Google_Open+Sans_400_sans | ||
| 18 | * Google_Open+Sans_400,700_sans | ||
| 19 | * Google_Open+Sans_400,700italic | ||
| 20 | * Google_Open+Sans_400,700italic_sans | ||
| 21 | * | ||
| 22 | * @returns {string} | ||
| 23 | */ | ||
| 24 | parser: function (fonts) { | ||
| 25 | var collection = [], | ||
| 26 | familyHash = {}; | ||
| 27 | |||
| 28 | 8 | Object.keys(fonts).forEach(function fontsIterator(key) { | |
| 29 | const font = fonts[key]; | ||
| 30 | 49 | var split = font.split('_'), | |
| 31 | familyKey = split[1], // Eg: Open+Sans | ||
| 32 | weights = split[2]; // Eg: 400,700italic | ||
| 33 | |||
| 34 | 49 | if (utils.isEmpty(familyKey)) { | |
| 35 | 6 | return; | |
| 36 | } | ||
| 37 | |||
| 38 | 43 | if (utils.isUndefined(weights)) { | |
| 39 | 13 | weights = ''; | |
| 40 | } | ||
| 41 | |||
| 42 | 43 | if (!utils.isArray(familyHash[familyKey])) { | |
| 43 | 33 | familyHash[familyKey] = []; | |
| 44 | } | ||
| 45 | |||
| 46 | 43 | weights = weights.split(','); | |
| 47 | |||
| 48 | 43 | familyHash[familyKey].push(weights); | |
| 49 | 43 | familyHash[familyKey] = [...new Set(familyHash[familyKey].flat())] | |
| 50 | }); | ||
| 51 | |||
| 52 | 8 | Object.keys(familyHash).forEach(function fontHashIterator(family) { | |
| 53 | const weights = familyHash[family]; | ||
| 54 | 33 | collection.push(family + ':' + weights.join(',')); | |
| 55 | }); | ||
| 56 | |||
| 57 | 8 | return collection; | |
| 58 | }, | ||
| 59 | |||
| 60 | buildLink: function (fonts, fontDisplay) { | ||
| 61 | const displayTypes = ['auto', 'block', 'swap', 'fallback', 'optional']; | ||
| 62 | 6 | fontDisplay = displayTypes.includes(fontDisplay) ? fontDisplay : 'swap'; | |
| 63 | 6 | const uri = `https://fonts.googleapis.com/css?family=${fonts.join('|')}&display=${fontDisplay}` | |
| 64 | 6 | try { | |
| 65 | 6 | const url = URL.parse(uri); | |
| 66 | 6 | return `<link href="${url.format()}" rel="stylesheet">`; | |
| 67 | } catch (error) { | ||
| 68 | throw new ValidationError(`Invalid URL [${uri}]. Check configured fonts.`) | ||
| 69 | } | ||
| 70 | }, | ||
| 71 | |||
| 72 | buildFontLoaderConfig: function (fonts) { | ||
| 73 | function replaceSpaces(font) { | ||
| 74 | 6 | return font.split('+').join(' '); | |
| 75 | } | ||
| 76 | |||
| 77 | 1 | return { | |
| 78 | google: { | ||
| 79 | families: fonts.map(font => replaceSpaces(font)), | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | }, | ||
| 83 | |||
| 84 | generateResourceHints: function (globals, fonts, fontDisplay) { | ||
| 85 | const displayTypes = ['auto', 'block', 'swap', 'fallback', 'optional']; | ||
| 86 | 6 | fontDisplay = displayTypes.includes(fontDisplay) ? fontDisplay : 'swap'; | |
| 87 | 6 | const path = `https://fonts.googleapis.com/css?family=${fonts.join('|')}&display=${fontDisplay}`; | |
| 88 | 6 | try { | |
| 89 | 6 | addResourceHint( | |
| 90 | globals, | ||
| 91 | path, | ||
| 92 | defaultResourceHintState, | ||
| 93 | resourceHintAllowedTypes.resourceHintStyleType | ||
| 94 | ); | ||
| 95 | } catch (e) { | ||
| 96 | console.info(`EarlyHint generation failed while generating fonts collection.`, e); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | }, | ||
| 100 | }; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Get collection of fonts used in theme settings. | ||
| 104 | * | ||
| 105 | * @param {string} format The desired return value. If format == 'providerLists', return an object with provider names for keys | ||
| 106 | * and a list of fonts in the provider format as values, suitable for use with Web Font Loader. If format == 'linkElements', | ||
| 107 | * return a string containing <link> elements to be directly inserted into the page. If format == 'webFontLoaderConfig', return an | ||
| 108 | * object that can be used to configure Web Font Loader. | ||
| 109 | * @param {Object} themeSettings Object containing theme settings. | ||
| 110 | * @param {Object} handlebars The handlebars instance. | ||
| 111 | * @param {Object} options an optional object for additional configuration details | ||
| 112 | * @returns {Object.<string, Array>|string} | ||
| 113 | */ | ||
| 114 | 1 | module.exports = function (format, themeSettings, handlebars, options) { | |
| 115 | |||
| 116 | const collectedFonts = {}; | ||
| 117 | 8 | Object.keys(themeSettings).forEach(function (key) { | |
| 118 | //check that -font is on end of string but not start of string | ||
| 119 | const fontKeySuffix = '-font'; | ||
| 120 | 54 | if (!key.endsWith(fontKeySuffix)) { | |
| 121 | 5 | return; | |
| 122 | } | ||
| 123 | |||
| 124 | 49 | const splits = themeSettings[key].split('_'); | |
| 125 | 49 | const provider = splits[0]; | |
| 126 | |||
| 127 | if ( typeof fontProviders[provider] === 'undefined' ) { |
||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | 49 | if (typeof collectedFonts[provider] === 'undefined') { | |
| 132 | 8 | collectedFonts[provider] = []; | |
| 133 | } | ||
| 134 | |||
| 135 | 49 | collectedFonts[provider].push(themeSettings[key]); | |
| 136 | }); | ||
| 137 | |||
| 138 | // Parse font strings based on provider | ||
| 139 | 8 | const parsedFonts = {}; | |
| 140 | 8 | Object.keys(collectedFonts).forEach(function (key) { | |
| 141 | parsedFonts[key] = fontProviders[key].parser(collectedFonts[key]); | ||
| 142 | }); | ||
| 143 | |||
| 144 | // Format output based on requested format | ||
| 145 | 8 | switch (format) { | |
| 146 | case 'linkElements': | ||
| 147 | 6 | const formattedFonts = {}; | |
| 148 | 6 | Object.keys(parsedFonts).forEach(function (key) { | |
| 149 | fontProviders[key].generateResourceHints(options.globals, parsedFonts[key], options.fontDisplay); | ||
| 150 | 6 | formattedFonts[key] = fontProviders[key].buildLink(parsedFonts[key], options.fontDisplay); | |
| 151 | }); | ||
| 152 | 6 | return new handlebars.SafeString(Object.values(formattedFonts).join('')); | |
| 153 | |||
| 154 | case 'webFontLoaderConfig': | ||
| 155 | // Build configs | ||
| 156 | 1 | const configs = {}; | |
| 157 | 1 | Object.keys(parsedFonts).forEach(function (key) { | |
| 158 | configs[key] = fontProviders[key].buildFontLoaderConfig(parsedFonts[key]); | ||
| 159 | }); | ||
| 160 | |||
| 161 | // Merge them | ||
| 162 | 1 | const fontLoaderConfig = {}; | |
| 163 | 1 | Object.values(configs).forEach((config) => { | |
| 164 | Object.assign(fontLoaderConfig, config); | ||
| 165 | }); | ||
| 166 | 1 | return fontLoaderConfig; | |
| 167 | |||
| 168 | case 'providerLists': | ||
| 169 | default: | ||
| 170 | 1 | return parsedFonts; | |
| 171 | } | ||
| 172 | } | ||
| 173 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | 1 | const utils = require('../3p/utils'); | |
| 3 | 1 | const common = require('./common'); | |
| 4 | |||
| 5 | 1 | const { ValidationError } = require('../../lib/errors'); | |
| 6 | |||
| 7 | 1 | const srcsets = { | |
| 8 | '80w': '80w', | ||
| 9 | '160w': '160w', | ||
| 10 | '320w': '320w', | ||
| 11 | '640w': '640w', | ||
| 12 | '960w': '960w', | ||
| 13 | '1280w': '1280w', | ||
| 14 | '1920w': '1920w', | ||
| 15 | '2560w': '2560w', | ||
| 16 | }; | ||
| 17 | |||
| 18 | function getObjectStorageImage(handlebars, cdnUrl, source, path, options) { | ||
| 19 | 34 | if (!utils.isString(path) || common.isValidURL(path)) { | |
| 20 | 4 | throw new ValidationError("Invalid image path - please use a filename or folder path starting from the appropriate folder"); | |
| 21 | } | ||
| 22 | |||
| 23 | // Return original image if there are no arguments | ||
| 24 | 30 | let size = 'original'; | |
| 25 | const lossy = options && options.hash && options.hash.lossy; |
||
| 26 | |||
| 27 | 30 | if (Number.isInteger(options.hash['width'])) { | |
| 28 | 12 | if (Number.isInteger(options.hash['height'])) { | |
| 29 | // Return image of specified size | ||
| 30 | 6 | size = `${options.hash['width']}x${options.hash['height']}` | |
| 31 | } else { | ||
| 32 | // Return image of specified width | ||
| 33 | 6 | size = `${options.hash['width']}w` | |
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | 30 | const baseUrl = `${cdnUrl}/images/stencil/${size}/${source}/${path}`; | |
| 38 | 30 | const finalUrl = common.appendLossyParam(baseUrl, lossy); | |
| 39 | 30 | return new handlebars.SafeString(finalUrl); | |
| 40 | } | ||
| 41 | |||
| 42 | function getObjectStorageImageSrcset(handlebars, cdnUrl, source, path, options) { | ||
| 43 | 14 | if (!utils.isString(path) || common.isValidURL(path)) { | |
| 44 | 4 | throw new ValidationError("Invalid image path - please use a filename or folder path starting from the appropriate folder"); | |
| 45 | } | ||
| 46 | |||
| 47 | const lossy = options && options.hash && options.hash.lossy; |
||
| 48 | |||
| 49 | 10 | return new handlebars.SafeString(Object.keys(srcsets).map(descriptor => { | |
| 50 | const baseUrl = `${cdnUrl}/images/stencil/${srcsets[descriptor]}/${source}/${path}`; | ||
| 51 | 80 | const finalUrl = common.appendLossyParam(baseUrl, lossy); | |
| 52 | 80 | return ([`${finalUrl} ${descriptor}`].join(' ')); | |
| 53 | }).join(', ')); | ||
| 54 | } | ||
| 55 | |||
| 56 | 1 | module.exports = { | |
| 57 | getObjectStorageImage, | ||
| 58 | getObjectStorageImageSrcset | ||
| 59 | }; | ||
| 60 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const utils = require("../3p/utils"); | |
| 2 | 1 | const URL = require('url'); | |
| 3 | |||
| 4 | 1 | const { ValidationError } = require('../../lib/errors'); | |
| 5 | |||
| 6 | 1 | const resourceHintsLimit = 50; | |
| 7 | |||
| 8 | 1 | const preloadResourceHintState = 'preload'; | |
| 9 | 1 | const preconnectResourceHintState = 'preconnect'; | |
| 10 | 1 | const prerenderResourceHintState = 'prerender'; | |
| 11 | 1 | const dnsPrefetchResourceHintState = 'dns-prefetch'; | |
| 12 | 1 | const resourceHintStates = [preloadResourceHintState, preconnectResourceHintState, prerenderResourceHintState, dnsPrefetchResourceHintState]; | |
| 13 | |||
| 14 | 1 | const resourceHintFontType = 'font'; | |
| 15 | 1 | const resourceHintStyleType = 'style'; | |
| 16 | 1 | const resourceHintScriptType = 'script'; | |
| 17 | 1 | const resourceHintDocumentType = 'document'; | |
| 18 | 1 | const resourceHintTypes = [resourceHintStyleType, resourceHintFontType, resourceHintScriptType, resourceHintDocumentType]; | |
| 19 | |||
| 20 | 1 | const noCors = 'no'; | |
| 21 | 1 | const anonymousCors = 'anonymous'; | |
| 22 | 1 | const useCredentialsCors = 'use-credentials'; | |
| 23 | 1 | const allowedCors = [noCors, anonymousCors, useCredentialsCors]; | |
| 24 | |||
| 25 | /** | ||
| 26 | * @param {string} path - The uri to the resource. | ||
| 27 | * @param {string} rel - any of [preload, preconnect, prerender, dns-prefetch] | ||
| 28 | * @param {string} type? - (as attr in HTML link tag) any of [style, font, script,document] If an invalid value is provided, property won't be included | ||
| 29 | * @param {string} cors? - (crossorigin attr in HTML tag) any of [no, anonymous, use-credentials] defaults to no when no value is provided | ||
| 30 | */ | ||
| 31 | function addResourceHint(globals, path, rel, type, cors) { | ||
| 32 | |||
| 33 | 40 | if (!utils.isString(path)) { | |
| 34 | 4 | throw new ValidationError('Invalid path provided. path should be a non empty string'); | |
| 35 | } | ||
| 36 | 36 | path = path.trim(); | |
| 37 | 36 | try { | |
| 38 | 36 | path = URL.parse(path).format(); | |
| 39 | } catch (error) { | ||
| 40 | throw new ValidationError(`Invalid value (resource URL) provided: ${path}`) | ||
| 41 | } | ||
| 42 | |||
| 43 | if ( !utils.isString(rel) || !resourceHintStates.includes(rel)) { |
||
| 44 | 2 | throw new ValidationError(`resourceHint attribute received invalid value. Valid values are: ${resourceHintStates}`); | |
| 45 | } | ||
| 46 | |||
| 47 | 34 | if (typeof globals.resourceHints === 'undefined') { | |
| 48 | 1 | globals.resourceHints = []; | |
| 49 | } | ||
| 50 | |||
| 51 | 34 | let index = globals.resourceHints.findIndex(({ src }) => path === src); | |
| 52 | 34 | if (index >= 0) { | |
| 53 | 6 | return path; | |
| 54 | } | ||
| 55 | |||
| 56 | 28 | if (globals.resourceHints.length >= resourceHintsLimit) { | |
| 57 | 1 | console.warn(`Resource hint for [${path}] due to the max limit of allowed hints was reached.`); | |
| 58 | 1 | return path; | |
| 59 | } | ||
| 60 | |||
| 61 | 27 | let hint = {src: path, state: rel}; | |
| 62 | |||
| 63 | 27 | if (utils.isString(type) && resourceHintTypes.includes(type)) { | |
| 64 | 24 | hint.type = type; | |
| 65 | } | ||
| 66 | |||
| 67 | 27 | if (utils.isString(cors) && !allowedCors.includes(cors)) { | |
| 68 | throw new ValidationError(`Invalid cors value provided. Valid values are: ${allowedCors}`); | ||
| 69 | 27 | } else if (!utils.isString(cors)) { | |
| 70 | 11 | cors = noCors; | |
| 71 | } | ||
| 72 | 27 | hint.cors = cors; | |
| 73 | |||
| 74 | 27 | globals.resourceHints.push(hint); | |
| 75 | |||
| 76 | 27 | return path; | |
| 77 | } | ||
| 78 | |||
| 79 | 1 | module.exports = { | |
| 80 | resourceHintsLimit, | ||
| 81 | defaultResourceHintState: preloadResourceHintState, | ||
| 82 | addResourceHint, | ||
| 83 | resourceHintAllowedTypes: { resourceHintStyleType, resourceHintFontType, resourceHintScriptType }, | ||
| 84 | resourceHintAllowedCors: { noCors, anonymousCors, useCredentialsCors }, | ||
| 85 | resourceHintAllowedStates: { | ||
| 86 | preloadResourceHintState, | ||
| 87 | preconnectResourceHintState, | ||
| 88 | prerenderResourceHintState, | ||
| 89 | dnsPrefetchResourceHintState | ||
| 90 | } | ||
| 91 | }; | ||
| 92 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | class AppError extends Error { | ||
| 4 | constructor(message, details) { | ||
| 5 | // Calling parent constructor of base Error class. | ||
| 6 | super(message); | ||
| 7 | |||
| 8 | // Saving class name in the property of our custom error as a shortcut. | ||
| 9 | 57 | this.name = this.constructor.name; | |
| 10 | |||
| 11 | // Capturing stack trace, excluding constructor call from it. | ||
| 12 | 57 | Error.captureStackTrace(this, this.constructor); | |
| 13 | |||
| 14 | // Store extra details for caller to diagnose error | ||
| 15 | 57 | this.details = details || {}; | |
| 16 | } | ||
| 17 | }; | ||
| 18 | |||
| 19 | 1 | module.exports = AppError; | |
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const AppError = require('./appError'); | |
| 2 | |||
| 3 | class CompileError extends AppError {}; // Error compiling template | ||
| 4 | class FormatError extends AppError {}; // Error restoring precompiled template | ||
| 5 | class RenderError extends AppError {}; // Error rendering template | ||
| 6 | class DecoratorError extends AppError {}; // Error applying decorator | ||
| 7 | class TemplateNotFoundError extends AppError {}; // Template not registered | ||
| 8 | class ValidationError extends Error {}; | ||
| 9 | class PrecompileError extends Error {}; | ||
| 10 | |||
| 11 | 1 | module.exports = { | |
| 12 | CompileError, | ||
| 13 | FormatError, | ||
| 14 | RenderError, | ||
| 15 | DecoratorError, | ||
| 16 | TemplateNotFoundError, | ||
| 17 | ValidationError, | ||
| 18 | PrecompileError | ||
| 19 | }; | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Code = require('code'); | |
| 4 | 1 | const Lab = require('lab'); | |
| 5 | 1 | const lab = exports.lab = Lab.script(); | |
| 6 | 1 | const expect = Code.expect; | |
| 7 | 1 | const it = lab.it; | |
| 8 | 1 | const describe = lab.describe; | |
| 9 | 1 | const beforeEach = lab.beforeEach; | |
| 10 | |||
| 11 | 1 | describe('helper registration', () => { | |
| 12 | let helpers; | ||
| 13 | |||
| 14 | 1 | beforeEach(done => { | |
| 15 | helpers = require('../helpers'); | ||
| 16 | 1 | done(); | |
| 17 | }); | ||
| 18 | |||
| 19 | 1 | it('loads all the helpers', done => { | |
| 20 | const expectedHelpers = [ | ||
| 21 | 'all', | ||
| 22 | 'any', | ||
| 23 | 'assignVar', | ||
| 24 | 'block', | ||
| 25 | 'cdn', | ||
| 26 | 'compare', | ||
| 27 | 'concat', | ||
| 28 | 'contains', | ||
| 29 | 'decrementVar', | ||
| 30 | 'dynamicComponent', | ||
| 31 | 'encodeHtmlEntities', | ||
| 32 | 'for', | ||
| 33 | 'get', | ||
| 34 | 'getContentImage', | ||
| 35 | 'getContentImageSrcset', | ||
| 36 | 'getFontLoaderConfig', | ||
| 37 | 'getFontsCollection', | ||
| 38 | 'getImage', | ||
| 39 | 'getImageManagerImage', | ||
| 40 | 'getImageManagerImageSrcset', | ||
| 41 | 'getImageSrcset', | ||
| 42 | 'getImageSrcset1x2x', | ||
| 43 | 'getObject', | ||
| 44 | 'getVar', | ||
| 45 | 'helperMissing', | ||
| 46 | 'if', | ||
| 47 | 'incrementVar', | ||
| 48 | 'inject', | ||
| 49 | 'join', | ||
| 50 | 'jsContext', | ||
| 51 | 'json', | ||
| 52 | 'lang', | ||
| 53 | 'langJson', | ||
| 54 | 'limit', | ||
| 55 | 'moment', | ||
| 56 | 'money', | ||
| 57 | 'multiConcat', | ||
| 58 | 'nl2br', | ||
| 59 | 'occurrences', | ||
| 60 | 'option', | ||
| 61 | 'or', | ||
| 62 | 'partial', | ||
| 63 | 'pluck', | ||
| 64 | 'pre', | ||
| 65 | 'region', | ||
| 66 | 'replace', | ||
| 67 | 'resourceHints', | ||
| 68 | 'setURLQueryParam', | ||
| 69 | 'snippet', | ||
| 70 | 'stripQuerystring', | ||
| 71 | 'strReplace', | ||
| 72 | 'stylesheet', | ||
| 73 | 'after', | ||
| 74 | 'arrayify', | ||
| 75 | 'before', | ||
| 76 | 'eachIndex', | ||
| 77 | 'filter', | ||
| 78 | 'first', | ||
| 79 | 'forEach', | ||
| 80 | 'inArray', | ||
| 81 | 'isArray', | ||
| 82 | 'last', | ||
| 83 | 'lengthEqual', | ||
| 84 | 'map', | ||
| 85 | 'some', | ||
| 86 | 'sort', | ||
| 87 | 'sortBy', | ||
| 88 | 'withAfter', | ||
| 89 | 'withBefore', | ||
| 90 | 'withFirst', | ||
| 91 | 'withLast', | ||
| 92 | 'withSort', | ||
| 93 | 'isEmpty', | ||
| 94 | 'iterate', | ||
| 95 | 'length', | ||
| 96 | 'and', | ||
| 97 | 'gt', | ||
| 98 | 'gte', | ||
| 99 | 'has', | ||
| 100 | 'eq', | ||
| 101 | 'ifEven', | ||
| 102 | 'ifNth', | ||
| 103 | 'ifOdd', | ||
| 104 | 'is', | ||
| 105 | 'isnt', | ||
| 106 | 'lt', | ||
| 107 | 'lte', | ||
| 108 | 'neither', | ||
| 109 | 'unlessEq', | ||
| 110 | 'unlessGt', | ||
| 111 | 'unlessLt', | ||
| 112 | 'unlessGteq', | ||
| 113 | 'unlessLteq', | ||
| 114 | 'ellipsis', | ||
| 115 | 'sanitize', | ||
| 116 | 'ul', | ||
| 117 | 'ol', | ||
| 118 | 'thumbnailImage', | ||
| 119 | 'inflect', | ||
| 120 | 'ordinalize', | ||
| 121 | 'markdown', | ||
| 122 | 'add', | ||
| 123 | 'subtract', | ||
| 124 | 'divide', | ||
| 125 | 'multiply', | ||
| 126 | 'floor', | ||
| 127 | 'ceil', | ||
| 128 | 'round', | ||
| 129 | 'sum', | ||
| 130 | 'avg', | ||
| 131 | 'default', | ||
| 132 | 'noop', | ||
| 133 | 'withHash', | ||
| 134 | 'addCommas', | ||
| 135 | 'phoneNumber', | ||
| 136 | 'random', | ||
| 137 | 'toAbbr', | ||
| 138 | 'toExponential', | ||
| 139 | 'toFixed', | ||
| 140 | 'toFloat', | ||
| 141 | 'toInt', | ||
| 142 | 'toPrecision', | ||
| 143 | 'extend', | ||
| 144 | 'forIn', | ||
| 145 | 'forOwn', | ||
| 146 | 'toPath', | ||
| 147 | 'hasOwn', | ||
| 148 | 'isObject', | ||
| 149 | 'merge', | ||
| 150 | 'JSONparseSafe', | ||
| 151 | 'JSONparse', | ||
| 152 | 'JSONstringify', | ||
| 153 | 'camelcase', | ||
| 154 | 'capitalize', | ||
| 155 | 'capitalizeAll', | ||
| 156 | 'center', | ||
| 157 | 'chop', | ||
| 158 | 'dashcase', | ||
| 159 | 'dotcase', | ||
| 160 | 'hyphenate', | ||
| 161 | 'isString', | ||
| 162 | 'lowercase', | ||
| 163 | 'pascalcase', | ||
| 164 | 'pathcase', | ||
| 165 | 'plusify', | ||
| 166 | 'reverse', | ||
| 167 | 'sentence', | ||
| 168 | 'snakecase', | ||
| 169 | 'split', | ||
| 170 | 'startsWith', | ||
| 171 | 'titleize', | ||
| 172 | 'trim', | ||
| 173 | 'uppercase', | ||
| 174 | 'encodeURI', | ||
| 175 | 'decodeURI', | ||
| 176 | 'urlResolve', | ||
| 177 | 'urlParse', | ||
| 178 | 'stripProtocol', | ||
| 179 | 'toLowerCase', | ||
| 180 | 'truncate', | ||
| 181 | 'unless', | ||
| 182 | 'enumerate', | ||
| 183 | 'equals', | ||
| 184 | 'getShortMonth', | ||
| 185 | 'pick', | ||
| 186 | 'earlyHint', | ||
| 187 | 'nonce', | ||
| 188 | 'typeof' | ||
| 189 | ].sort(); | ||
| 190 | |||
| 191 | 1 | expect(helpers.map(helper => helper.name).sort()).to.be.equal(expectedHelpers); | |
| 192 | 1 | done(); | |
| 193 | }); | ||
| 194 | }); | ||
| 195 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Sinon = require('sinon'); | |
| 4 | 1 | const Code = require('code'); | |
| 5 | 1 | const Lab = require('lab'); | |
| 6 | 1 | const lab = exports.lab = Lab.script(); | |
| 7 | 1 | const expect = Code.expect; | |
| 8 | 1 | const it = lab.it; | |
| 9 | 1 | const describe = lab.describe; | |
| 10 | 1 | const beforeEach = lab.beforeEach; | |
| 11 | 1 | const afterEach = lab.afterEach; | |
| 12 | |||
| 13 | 1 | const helpers = require('./spec-helpers'); | |
| 14 | 1 | const Handlebars = require('handlebars'); | |
| 15 | 1 | const HandlebarsRenderer = require('../index'); | |
| 16 | |||
| 17 | 1 | describe('switching handlebars versions', () => { | |
| 18 | it('defaults to v3', done => { | ||
| 19 | const renderer = new HandlebarsRenderer(); | ||
| 20 | 1 | expect(renderer.handlebars.VERSION.substring(0, 1)).to.equal('3'); | |
| 21 | 1 | done(); | |
| 22 | }); | ||
| 23 | |||
| 24 | 1 | it('can load v3', done => { | |
| 25 | const renderer = new HandlebarsRenderer({}, {}, 'v3'); | ||
| 26 | 1 | expect(renderer.handlebars.VERSION.substring(0, 1)).to.equal('3'); | |
| 27 | 1 | done(); | |
| 28 | }); | ||
| 29 | |||
| 30 | 1 | it('can load v4', done => { | |
| 31 | const renderer = new HandlebarsRenderer({}, {}, 'v4'); | ||
| 32 | 1 | expect(renderer.handlebars.VERSION.substring(0, 1)).to.equal('4'); | |
| 33 | 1 | done(); | |
| 34 | }); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | describe('helper registration', () => { | |
| 38 | let renderer; | ||
| 39 | |||
| 40 | 1 | beforeEach(done => { | |
| 41 | renderer = new HandlebarsRenderer(); | ||
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('loads the helpers and registers them with handlebars', done => { | |
| 46 | // Handlebars loads some default helpers, so let's find out how many | ||
| 47 | const handlebars = Handlebars.create(); | ||
| 48 | 1 | const defaultHelpers = Object.keys(handlebars.helpers).length; | |
| 49 | 1 | expect(defaultHelpers).to.be.greaterThan(0); | |
| 50 | |||
| 51 | // We already test that the expected helpers load properly in the helpers spec, | ||
| 52 | // so here let's just test that we register extra helpers above and beyond the defaults. | ||
| 53 | 1 | expect(Object.keys(renderer.handlebars.helpers).length).to.be.greaterThan(defaultHelpers); | |
| 54 | 1 | done(); | |
| 55 | }); | ||
| 56 | }); | ||
| 57 | |||
| 58 | 1 | describe('helper context', () => { | |
| 59 | let renderer, siteSettings, themeSettings; | ||
| 60 | |||
| 61 | 1 | beforeEach(done => { | |
| 62 | renderer = new HandlebarsRenderer(); | ||
| 63 | 10 | done(); | |
| 64 | }); | ||
| 65 | |||
| 66 | 1 | it('puts empty site settings into the helper context when not provided', done => { | |
| 67 | expect(renderer.helperContext.getSiteSettings()).to.equal({}); | ||
| 68 | 1 | done(); | |
| 69 | }); | ||
| 70 | |||
| 71 | 1 | it('puts site settings into the helper context when provided', done => { | |
| 72 | siteSettings = { foo: 'bar' }; | ||
| 73 | 1 | renderer = new HandlebarsRenderer(siteSettings); | |
| 74 | 1 | expect(renderer.helperContext.getSiteSettings().foo).to.equal('bar'); | |
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('puts site settings into the helper context when provided after construction', done => { | |
| 79 | siteSettings = { foo: 'bar' }; | ||
| 80 | 1 | renderer = new HandlebarsRenderer(); | |
| 81 | 1 | renderer.setSiteSettings(siteSettings); | |
| 82 | 1 | expect(renderer.helperContext.getSiteSettings().foo).to.equal('bar'); | |
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | |||
| 86 | 1 | it('puts empty theme settings into the helper context when not provided', done => { | |
| 87 | expect(renderer.helperContext.getThemeSettings()).to.equal({}); | ||
| 88 | 1 | done(); | |
| 89 | }); | ||
| 90 | |||
| 91 | 1 | it('puts theme settings into the helper context when provided', done => { | |
| 92 | themeSettings = { foo: 'bar' }; | ||
| 93 | 1 | renderer = new HandlebarsRenderer({}, themeSettings); | |
| 94 | 1 | expect(renderer.helperContext.getThemeSettings().foo).to.equal('bar'); | |
| 95 | 1 | done(); | |
| 96 | }); | ||
| 97 | |||
| 98 | 1 | it('puts theme settings into the helper context when provided after construction', done => { | |
| 99 | themeSettings = { foo: 'bar' }; | ||
| 100 | 1 | renderer = new HandlebarsRenderer(); | |
| 101 | 1 | renderer.setThemeSettings(themeSettings); | |
| 102 | 1 | expect(renderer.helperContext.getThemeSettings().foo).to.equal('bar'); | |
| 103 | 1 | done(); | |
| 104 | }); | ||
| 105 | |||
| 106 | 1 | it('puts translator accessor into the helper context', done => { | |
| 107 | const translator = { foo: 'bar' }; | ||
| 108 | 1 | renderer.setTranslator(translator); | |
| 109 | 1 | expect(renderer.helperContext.getTranslator()).to.equal(translator); | |
| 110 | 1 | done(); | |
| 111 | }); | ||
| 112 | |||
| 113 | 1 | it('puts page content accessor into the helper context', done => { | |
| 114 | const content = { foo: 'bar' }; | ||
| 115 | 1 | renderer.setContent(content); | |
| 116 | 1 | expect(renderer.helperContext.getContent()).to.equal(content); | |
| 117 | 1 | done(); | |
| 118 | }); | ||
| 119 | |||
| 120 | 1 | it('puts handlebars environment into the helper context', done => { | |
| 121 | expect(renderer.helperContext.handlebars).to.not.be.undefined(); | ||
| 122 | 1 | expect(renderer.helperContext.handlebars.helpers).to.not.be.empty(); | |
| 123 | 1 | done(); | |
| 124 | }); | ||
| 125 | |||
| 126 | 1 | it('puts a handle to global storage into the helper context', done => { | |
| 127 | expect(renderer.helperContext.storage).to.not.be.undefined(); | ||
| 128 | 1 | done(); | |
| 129 | }); | ||
| 130 | }); | ||
| 131 | |||
| 132 | 1 | describe('addTemplates', () => { | |
| 133 | let renderer, sandbox; | ||
| 134 | 1 | const templates = { | |
| 135 | 'foo': '{{bar}}', | ||
| 136 | 'baz': '{{bat}}', | ||
| 137 | }; | ||
| 138 | |||
| 139 | 1 | beforeEach(done => { | |
| 140 | sandbox = Sinon.createSandbox(); | ||
| 141 | 8 | renderer = new HandlebarsRenderer(); | |
| 142 | 8 | done(); | |
| 143 | }); | ||
| 144 | |||
| 145 | 1 | afterEach(done => { | |
| 146 | sandbox.restore(); | ||
| 147 | 8 | done(); | |
| 148 | }); | ||
| 149 | |||
| 150 | 1 | it('registers preprocessed templates with handlebars', done => { | |
| 151 | const processor = renderer.getPreProcessor(); | ||
| 152 | 1 | renderer.addTemplates(processor(templates)); | |
| 153 | 1 | expect(renderer.handlebars.partials['foo']).to.not.be.undefined(); | |
| 154 | 1 | expect(renderer.handlebars.partials['baz']).to.not.be.undefined(); | |
| 155 | 1 | done(); | |
| 156 | }); | ||
| 157 | |||
| 158 | 1 | it('registers raw templates with handlebars', done => { | |
| 159 | renderer.addTemplates(templates); | ||
| 160 | 1 | expect(renderer.handlebars.partials['foo']).to.not.be.undefined(); | |
| 161 | 1 | expect(renderer.handlebars.partials['baz']).to.not.be.undefined(); | |
| 162 | 1 | done(); | |
| 163 | }); | ||
| 164 | |||
| 165 | 1 | it('registers a mix of preprocessed and raw templates with handlebars', done => { | |
| 166 | const processor = renderer.getPreProcessor(); | ||
| 167 | 1 | const processedTemplates = processor(templates); | |
| 168 | 1 | renderer.addTemplates(Object.assign({}, processedTemplates, { 'bat': '{{bob}}' })); | |
| 169 | 1 | expect(renderer.handlebars.partials['foo']).to.not.be.undefined(); | |
| 170 | 1 | expect(renderer.handlebars.partials['baz']).to.not.be.undefined(); | |
| 171 | 1 | expect(renderer.handlebars.partials['bat']).to.not.be.undefined(); | |
| 172 | 1 | done(); | |
| 173 | }); | ||
| 174 | |||
| 175 | 1 | it('throws FormatError if templates were precompiled with the wrong compiler version', done => { | |
| 176 | const processor = renderer.getPreProcessor(); | ||
| 177 | 1 | const processedTemplates = processor(templates); | |
| 178 | 1 | processedTemplates['baz'] = processedTemplates['baz'].replace(/\[6,">= 2.0.0-beta.1"\]/, '[7,">= 4.0"]'); | |
| 179 | 1 | try { | |
| 180 | 1 | renderer.addTemplates(processedTemplates); | |
| 181 | } catch(e) { | ||
| 182 | 1 | expect(e instanceof HandlebarsRenderer.errors.FormatError).to.be.true(); | |
| 183 | } | ||
| 184 | 1 | done(); | |
| 185 | }); | ||
| 186 | |||
| 187 | 1 | it('preProcessor throws CompileError when given bad templates', done => { | |
| 188 | const processor = renderer.getPreProcessor(); | ||
| 189 | 1 | try { | |
| 190 | 1 | processor(Object.assign({}, templates, { 'bat': '{{' })); | |
| 191 | } catch(e) { | ||
| 192 | 1 | expect(e instanceof HandlebarsRenderer.errors.CompileError).to.be.true(); | |
| 193 | 1 | expect(e.details.path).to.equal('bat'); | |
| 194 | } | ||
| 195 | 1 | done(); | |
| 196 | }); | ||
| 197 | |||
| 198 | 1 | it('can render using registered partials', done => { | |
| 199 | const processor = renderer.getPreProcessor(); | ||
| 200 | 1 | renderer.addTemplates(processor(templates)); | |
| 201 | 1 | renderer.render('baz', { bat: '123' }).then(result => { | |
| 202 | expect(result).to.equal('123'); | ||
| 203 | 1 | done(); | |
| 204 | }); | ||
| 205 | }); | ||
| 206 | |||
| 207 | 1 | it('isTemplateLoaded tells you if a template has been loaded', done => { | |
| 208 | const processor = renderer.getPreProcessor(); | ||
| 209 | 1 | renderer.addTemplates(processor(templates)); | |
| 210 | 1 | expect(renderer.isTemplateLoaded('baz')).to.be.true(); | |
| 211 | 1 | done(); | |
| 212 | }); | ||
| 213 | |||
| 214 | 1 | it('does not override templates if called twice', done => { | |
| 215 | const newTemplates = { | ||
| 216 | 'baz': 'no variables', | ||
| 217 | }; | ||
| 218 | |||
| 219 | 1 | const processor = renderer.getPreProcessor(); | |
| 220 | 1 | renderer.addTemplates(processor(templates)); | |
| 221 | 1 | renderer.addTemplates(processor(newTemplates)); | |
| 222 | 1 | renderer.render('baz', { bat: '123' }).then(result => { | |
| 223 | expect(result).to.equal('123'); | ||
| 224 | 1 | done(); | |
| 225 | }); | ||
| 226 | }); | ||
| 227 | }); | ||
| 228 | |||
| 229 | 1 | describe('render', () => { | |
| 230 | let renderer, sandbox; | ||
| 231 | 1 | const templates = { | |
| 232 | 'foo': '{{bar}}', | ||
| 233 | 'capitalize_foo': '{{capitalize bar}}', | ||
| 234 | 'with_locale': '{{locale_name}}', | ||
| 235 | 'with_template': '{{template}}', | ||
| 236 | }; | ||
| 237 | 1 | const context = { | |
| 238 | bar: 'baz' | ||
| 239 | }; | ||
| 240 | |||
| 241 | 1 | beforeEach(done => { | |
| 242 | sandbox = Sinon.createSandbox(); | ||
| 243 | 9 | renderer = new HandlebarsRenderer(); | |
| 244 | 9 | const processor = renderer.getPreProcessor(); | |
| 245 | 9 | renderer.addTemplates(processor(templates)); | |
| 246 | 9 | done(); | |
| 247 | }); | ||
| 248 | |||
| 249 | 1 | afterEach(done => { | |
| 250 | sandbox.restore(); | ||
| 251 | 9 | done(); | |
| 252 | }); | ||
| 253 | |||
| 254 | 1 | it('can render using registered partials', done => { | |
| 255 | renderer.render('foo', context).then(result => { | ||
| 256 | expect(result).to.equal('baz'); | ||
| 257 | 1 | done(); | |
| 258 | }); | ||
| 259 | }); | ||
| 260 | |||
| 261 | 1 | it('renders without a context', done => { | |
| 262 | renderer.render('foo').then(result => { | ||
| 263 | expect(result).to.equal(''); | ||
| 264 | 1 | done(); | |
| 265 | }); | ||
| 266 | }); | ||
| 267 | |||
| 268 | 1 | it('sets the locale in the context if a translator is supplied', done => { | |
| 269 | const translator = { | ||
| 270 | getLocale: () => 'en', | ||
| 271 | }; | ||
| 272 | 1 | renderer.setTranslator(translator); | |
| 273 | 1 | renderer.render('with_locale', context).then(result => { | |
| 274 | expect(result).to.equal('en'); | ||
| 275 | 1 | done(); | |
| 276 | }); | ||
| 277 | }); | ||
| 278 | |||
| 279 | 1 | it('sets the template path in the context', done => { | |
| 280 | renderer.render('with_template', context).then(result => { | ||
| 281 | expect(result).to.equal('with_template'); | ||
| 282 | 1 | done(); | |
| 283 | }); | ||
| 284 | }); | ||
| 285 | |||
| 286 | 1 | it('applies decorators', done => { | |
| 287 | renderer.addDecorator(output => { | ||
| 288 | return `<wrap>${output}</wrap>`; | ||
| 289 | }); | ||
| 290 | 1 | renderer.render('foo', context).then(result => { | |
| 291 | expect(result).to.equal('<wrap>baz</wrap>'); | ||
| 292 | 1 | done(); | |
| 293 | }); | ||
| 294 | }); | ||
| 295 | |||
| 296 | 1 | it('can use helpers', done => { | |
| 297 | renderer.render('capitalize_foo', context).then(result => { | ||
| 298 | expect(result).to.equal('Baz'); | ||
| 299 | 1 | done(); | |
| 300 | }); | ||
| 301 | }); | ||
| 302 | |||
| 303 | 1 | it('throws TemplateNotFound if missing template', done => { | |
| 304 | renderer.render('nonexistent-template', context).catch(e => { | ||
| 305 | expect(e instanceof HandlebarsRenderer.errors.TemplateNotFoundError).to.be.true(); | ||
| 306 | 1 | done(); | |
| 307 | }); | ||
| 308 | }); | ||
| 309 | |||
| 310 | 1 | it('throws RenderError if given bad template', done => { | |
| 311 | renderer.addTemplates({'bad-template': '{{'}); | ||
| 312 | 1 | renderer.render('bad-template', context).catch(e => { | |
| 313 | expect(e instanceof HandlebarsRenderer.errors.RenderError).to.be.true(); | ||
| 314 | 1 | done(); | |
| 315 | }); | ||
| 316 | }); | ||
| 317 | |||
| 318 | 1 | it('throws DecoratorError if decorator fails', done => { | |
| 319 | renderer.addDecorator(output => { | ||
| 320 | throw Error(); | ||
| 321 | }); | ||
| 322 | |||
| 323 | 1 | renderer.render('foo', context).catch(e => { | |
| 324 | expect(e instanceof HandlebarsRenderer.errors.DecoratorError).to.be.true(); | ||
| 325 | 1 | done(); | |
| 326 | }); | ||
| 327 | }); | ||
| 328 | }); | ||
| 329 | |||
| 330 | 1 | describe('renderString', () => { | |
| 331 | let renderer, sandbox; | ||
| 332 | 1 | const context = { | |
| 333 | bar: 'baz' | ||
| 334 | }; | ||
| 335 | |||
| 336 | 1 | beforeEach(done => { | |
| 337 | sandbox = Sinon.createSandbox(); | ||
| 338 | 5 | renderer = new HandlebarsRenderer(); | |
| 339 | 5 | done(); | |
| 340 | }); | ||
| 341 | |||
| 342 | 1 | afterEach(done => { | |
| 343 | sandbox.restore(); | ||
| 344 | 5 | done(); | |
| 345 | }); | ||
| 346 | |||
| 347 | 1 | it('renders the given template with the given context', done => { | |
| 348 | renderer.renderString('<foo>{{bar}}</foo>', context).then(result => { | ||
| 349 | expect(result).to.equal('<foo>baz</foo>'); | ||
| 350 | 1 | done(); | |
| 351 | }); | ||
| 352 | }); | ||
| 353 | |||
| 354 | 1 | it('can use helpers', done => { | |
| 355 | renderer.renderString('<foo>{{capitalize bar}}</foo>', context).then(result => { | ||
| 356 | expect(result).to.equal('<foo>Baz</foo>'); | ||
| 357 | 1 | done(); | |
| 358 | }); | ||
| 359 | }); | ||
| 360 | |||
| 361 | 1 | it('renders without a context', done => { | |
| 362 | renderer.renderString('foo', context).then(result => { | ||
| 363 | expect(result).to.equal('foo'); | ||
| 364 | 1 | done(); | |
| 365 | }); | ||
| 366 | }); | ||
| 367 | |||
| 368 | 1 | it('throws CompileError if given a non-string template', done => { | |
| 369 | renderer.renderString(helpers.randomInt(), context).catch(e => { | ||
| 370 | expect(e instanceof HandlebarsRenderer.errors.CompileError).to.be.true(); | ||
| 371 | 1 | done(); | |
| 372 | }); | ||
| 373 | }); | ||
| 374 | |||
| 375 | 1 | it('throws PrecompileError if given malformed template', done => { | |
| 376 | renderer.renderString('{{', context).catch(e => { | ||
| 377 | expect(e instanceof HandlebarsRenderer.errors.PrecompileError).to.be.true(); | ||
| 378 | 1 | done(); | |
| 379 | }); | ||
| 380 | }); | ||
| 381 | }); | ||
| 382 | |||
| 383 | 1 | describe('errors', () => { | |
| 384 | let sandbox; | ||
| 385 | |||
| 386 | 1 | beforeEach(done => { | |
| 387 | sandbox = Sinon.createSandbox(); | ||
| 388 | 1 | done(); | |
| 389 | }); | ||
| 390 | |||
| 391 | 1 | afterEach(done => { | |
| 392 | sandbox.restore(); | ||
| 393 | 1 | done(); | |
| 394 | }); | ||
| 395 | |||
| 396 | 1 | it('has an accessor to get custom error classes', done => { | |
| 397 | expect(HandlebarsRenderer.errors).to.not.be.empty(); | ||
| 398 | 1 | expect(HandlebarsRenderer.errors.CompileError.prototype instanceof Error).to.be.true(); | |
| 399 | 1 | expect(HandlebarsRenderer.errors.FormatError.prototype instanceof Error).to.be.true(); | |
| 400 | 1 | expect(HandlebarsRenderer.errors.RenderError.prototype instanceof Error).to.be.true(); | |
| 401 | 1 | expect(HandlebarsRenderer.errors.DecoratorError.prototype instanceof Error).to.be.true(); | |
| 402 | 1 | expect(HandlebarsRenderer.errors.TemplateNotFoundError.prototype instanceof Error).to.be.true(); | |
| 403 | 1 | done(); | |
| 404 | }); | ||
| 405 | }); | ||
| 406 | |||
| 407 | 1 | describe('logging', () => { | |
| 408 | let renderer, logger, sandbox; | ||
| 409 | 1 | let consoleLogCopy = console.log; | |
| 410 | 1 | let consoleInfoCopy = console.info; | |
| 411 | 1 | let consoleErrorCopy = console.error; | |
| 412 | 1 | let consoleWarnCopy = console.warn; | |
| 413 | 1 | const context = { | |
| 414 | bar: 'baz' | ||
| 415 | }; | ||
| 416 | |||
| 417 | 1 | beforeEach(done => { | |
| 418 | sandbox = Sinon.createSandbox(); | ||
| 419 | 12 | logger = { | |
| 420 | info: Sinon.fake(), | ||
| 421 | warn: Sinon.fake(), | ||
| 422 | error: Sinon.fake(), | ||
| 423 | }; | ||
| 424 | 12 | console = { | |
| 425 | log: Sinon.fake(), | ||
| 426 | }; | ||
| 427 | 12 | done(); | |
| 428 | }); | ||
| 429 | |||
| 430 | 1 | afterEach(done => { | |
| 431 | console.log = consoleLogCopy; | ||
| 432 | 12 | console.info = consoleInfoCopy; | |
| 433 | 12 | console.error = consoleErrorCopy; | |
| 434 | 12 | console.warn = consoleWarnCopy; | |
| 435 | 12 | sandbox.restore(); | |
| 436 | 12 | done(); | |
| 437 | }); | ||
| 438 | |||
| 439 | 1 | it('log helper uses given logger', done => { | |
| 440 | renderer = new HandlebarsRenderer({}, {}, 'v3', logger); | ||
| 441 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 442 | expect(logger.info.called).to.equal(false); | ||
| 443 | 1 | done(); | |
| 444 | }); | ||
| 445 | }); | ||
| 446 | |||
| 447 | 1 | it('log helper should not be called, when log level = error', done => { | |
| 448 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger, 'error'); | ||
| 449 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 450 | expect(logger.info.called).to.equal(false); | ||
| 451 | 1 | done(); | |
| 452 | }); | ||
| 453 | }); | ||
| 454 | |||
| 455 | 1 | it('log helper should not be called, when log level = warning', done => { | |
| 456 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger, 'warning'); | ||
| 457 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 458 | expect(logger.info.called).to.equal(false); | ||
| 459 | 1 | done(); | |
| 460 | }); | ||
| 461 | }); | ||
| 462 | |||
| 463 | |||
| 464 | 1 | it('console log helper uses given logger', done => { | |
| 465 | renderer = new HandlebarsRenderer({}, {}, 'v4', console); | ||
| 466 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 467 | expect(console.log.calledWith('baz')).to.equal(true); | ||
| 468 | 1 | done(); | |
| 469 | }); | ||
| 470 | }); | ||
| 471 | |||
| 472 | 1 | it('console log helper should not be called, when log level = error', done => { | |
| 473 | renderer = new HandlebarsRenderer({}, {}, 'v4', console, 'error'); | ||
| 474 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 475 | expect(console.log.called).to.equal(false); | ||
| 476 | 1 | done(); | |
| 477 | }); | ||
| 478 | }); | ||
| 479 | |||
| 480 | 1 | it('console log helper should not be called, when log level = warning', done => { | |
| 481 | renderer = new HandlebarsRenderer({}, {}, 'v4', console, 'warning'); | ||
| 482 | 1 | renderer.renderString('{{log bar}}', context).then(() => { | |
| 483 | expect(console.log.called).to.equal(false); | ||
| 484 | 1 | done(); | |
| 485 | }); | ||
| 486 | }); | ||
| 487 | |||
| 488 | 1 | it('should override default console.log', done => { | |
| 489 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 490 | 1 | console.log('test'); | |
| 491 | 1 | expect(logger.info.calledWith('test')).to.equal(true); | |
| 492 | 1 | done(); | |
| 493 | }); | ||
| 494 | |||
| 495 | 1 | it('should override default console.info', done => { | |
| 496 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 497 | 1 | console.info('info'); | |
| 498 | 1 | expect(logger.info.calledWith('info')).to.equal(true); | |
| 499 | 1 | done(); | |
| 500 | }); | ||
| 501 | |||
| 502 | 1 | it('should override default console.error', done => { | |
| 503 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 504 | 1 | console.error('error'); | |
| 505 | 1 | expect(logger.error.calledWith('error')).to.equal(true); | |
| 506 | 1 | done(); | |
| 507 | }); | ||
| 508 | |||
| 509 | 1 | it('should override default console.warn', done => { | |
| 510 | renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 511 | 1 | console.warn('test'); | |
| 512 | 1 | expect(logger.warn.calledWith('test')).to.equal(true); | |
| 513 | 1 | done(); | |
| 514 | }); | ||
| 515 | |||
| 516 | 1 | it('should check that property access denied message is set as info', async () => { | |
| 517 | const renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 518 | 1 | const result = await renderer.renderString("{{aString.trim}}", { aString: " abc " }); | |
| 519 | 1 | expect(result).to.equal(""); | |
| 520 | 1 | expect(logger.info.calledWith('Handlebars: Access has been denied to resolve the property "trim" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details')) | |
| 521 | .to.equal(true); | ||
| 522 | |||
| 523 | }); | ||
| 524 | |||
| 525 | 1 | it('should check that property access denied message is set as error', async () => { | |
| 526 | console.error = Sinon.fake(); | ||
| 527 | 1 | const renderer = new HandlebarsRenderer({}, {}, 'v4', console); | |
| 528 | 1 | const result = await renderer.renderString("{{aString.toLowerCase}}", { aString: " abc " }); | |
| 529 | 1 | expect(result).to.equal(""); | |
| 530 | 1 | expect(console.error.calledWith('Handlebars: Access has been denied to resolve the property "toLowerCase" because it is not an "own property" of its parent.\n' + 'You can add a runtime option to disable the check or this warning:\n' + 'See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details')) | |
| 531 | .to.equal(true); | ||
| 532 | }); | ||
| 533 | }); | ||
| 534 | |||
| 535 | // STRF-12276 | ||
| 536 | 1 | describe('object manipulation fix', () => { | |
| 537 | let sandbox, logger; | ||
| 538 | 1 | let consoleErrorCopy = console.error; | |
| 539 | 1 | const templateString = `{{#JSONparse}} '{"a":"b"}'{{/JSONparse}}`; | |
| 540 | |||
| 541 | 1 | beforeEach(done => { | |
| 542 | sandbox = Sinon.createSandbox(); | ||
| 543 | 2 | logger = { | |
| 544 | info: Sinon.fake(), | ||
| 545 | warn: Sinon.fake(), | ||
| 546 | error: Sinon.fake(), | ||
| 547 | }; | ||
| 548 | 2 | console = { | |
| 549 | log: console.log, | ||
| 550 | error: Sinon.fake(), | ||
| 551 | }; | ||
| 552 | 2 | done(); | |
| 553 | }); | ||
| 554 | |||
| 555 | 1 | afterEach(done => { | |
| 556 | console.error = consoleErrorCopy; | ||
| 557 | 2 | sandbox.restore(); | |
| 558 | 2 | done(); | |
| 559 | }); | ||
| 560 | |||
| 561 | 1 | it('shouldnt print when use renderString', async () => { | |
| 562 | const renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 563 | 1 | try { | |
| 564 | 1 | await renderer.renderString(templateString, {}); | |
| 565 | expect(logger.error.called()).to.equal(false); | ||
| 566 | } catch (e) { | ||
| 567 | 1 | expect(e instanceof HandlebarsRenderer.errors.RenderError).to.be.true(); | |
| 568 | } | ||
| 569 | }); | ||
| 570 | |||
| 571 | 1 | it('shouldnt print when use render', async () => { | |
| 572 | const renderer = new HandlebarsRenderer({}, {}, 'v4', logger); | ||
| 573 | 1 | try { | |
| 574 | 1 | const templates = {'foo': templateString }; | |
| 575 | |||
| 576 | 1 | const processor = renderer.getPreProcessor(); | |
| 577 | 1 | renderer.addTemplates(processor(templates)); | |
| 578 | 1 | await renderer.render('foo', {}); | |
| 579 | expect(logger.error.called()).to.equal(false); | ||
| 580 | } catch (e) { | ||
| 581 | 1 | expect(e instanceof HandlebarsRenderer.errors.RenderError).to.be.true(); | |
| 582 | } | ||
| 583 | }); | ||
| 584 | }); | ||
| 585 | |||
| 586 | |||
| 587 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Crypto = require('crypto'); | |
| 2 | 1 | const Renderer = require('../index'); | |
| 3 | 1 | const expect = require('code').expect; | |
| 4 | |||
| 5 | function buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams) { | ||
| 6 | 623 | siteSettings = siteSettings || {}; | |
| 7 | 623 | themeSettings = themeSettings || {}; | |
| 8 | 623 | hbVersion = hbVersion || 'v3'; | |
| 9 | 623 | requestParams = requestParams || {}; | |
| 10 | 623 | const renderer = new Renderer(siteSettings, themeSettings, hbVersion, console, 'info', requestParams); | |
| 11 | |||
| 12 | // Register templates | ||
| 13 | 623 | if (typeof templates !== 'undefined') { | |
| 14 | 22 | templates = renderer.getPreProcessor()(templates); | |
| 15 | 22 | renderer.addTemplates(templates); | |
| 16 | } | ||
| 17 | |||
| 18 | 623 | return renderer; | |
| 19 | } | ||
| 20 | |||
| 21 | function renderString(template, context, siteSettings, themeSettings, templates, hbVersion, requestParams) { | ||
| 22 | 47 | const renderer = buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams); | |
| 23 | 47 | return renderer.renderString(template, context); | |
| 24 | } | ||
| 25 | |||
| 26 | function render(template, context, siteSettings, themeSettings, templates, hbVersion, requestParams) { | ||
| 27 | 6 | const renderer = buildRenderer(siteSettings, themeSettings, templates, hbVersion, requestParams); | |
| 28 | 6 | return renderer.render(template, context); | |
| 29 | } | ||
| 30 | |||
| 31 | // Return a function that is used to run through a set of test cases using renderString | ||
| 32 | function testRunner({context, siteSettings, themeSettings, templates, renderer, hbVersion, requestParams}) { | ||
| 33 | 78 | return (testCases, done) => { | |
| 34 | const promises = testCases.map(testCase => { | ||
| 35 | const render = testCase.renderer || | ||
| 36 | renderer || | ||
| 37 | buildRenderer(testCase.siteSettings || siteSettings, | ||
| 38 | testCase.themeSettings || themeSettings, | ||
| 39 | testCase.templates || templates, |
||
| 40 | testCase.hbVersion || hbVersion, |
||
| 41 | testCase.requestParams || requestParams); |
||
| 42 | |||
| 43 | 560 | return render.renderString(testCase.input, testCase.context || context).then(result => { | |
| 44 | expect(result).to.be.equal(testCase.output); | ||
| 45 | }); | ||
| 46 | }); | ||
| 47 | |||
| 48 | // Return a promise to run all the given test cases, then call done | ||
| 49 | 252 | return Promise.all(promises).then(() => { done(); }); | |
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | // Return a random string | ||
| 54 | function randomString() { | ||
| 55 | 5 | return Crypto.randomBytes(10).toString('hex'); | |
| 56 | } | ||
| 57 | |||
| 58 | // Return a random integer | ||
| 59 | function randomInt(low, high) { | ||
| 60 | low = low || 0; |
||
| 61 | high = high || 1e9; |
||
| 62 | 1 | return Math.floor(Math.random() * (high - low) + low); | |
| 63 | } | ||
| 64 | |||
| 65 | 1 | module.exports = { | |
| 66 | buildRenderer, | ||
| 67 | renderString, | ||
| 68 | render, | ||
| 69 | testRunner, | ||
| 70 | randomString, | ||
| 71 | randomInt, | ||
| 72 | }; | ||
| 73 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('all helper', function() { | |
| 8 | const context = { | ||
| 9 | num1: 1, | ||
| 10 | num2: 2, | ||
| 11 | product: {a: 1, b: 2}, | ||
| 12 | string: 'yolo', | ||
| 13 | alwaysTrue: true, | ||
| 14 | alwaysFalse: false, | ||
| 15 | emptyArray: [], | ||
| 16 | emptyObject: {}, | ||
| 17 | itemArray: [1,2], | ||
| 18 | emptyString: "", | ||
| 19 | big: 'big', | ||
| 20 | alwaysNull: null, | ||
| 21 | }; | ||
| 22 | |||
| 23 | // Build a test runner that uses a default context | ||
| 24 | 1 | const runTestCases = testRunner({context}); | |
| 25 | |||
| 26 | 1 | it('(with single argument) should behave like if helper', function(done) { | |
| 27 | runTestCases([ | ||
| 28 | { | ||
| 29 | input: '{{#all 1}}{{big}}{{/all}}', | ||
| 30 | output: 'big', | ||
| 31 | }, | ||
| 32 | { | ||
| 33 | input: '{{#all 1}}big{{/all}}', | ||
| 34 | output: 'big', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: '{{#all "x"}}big{{/all}}', | ||
| 38 | output: 'big', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{#all ""}}big{{/all}}', | ||
| 42 | output: '', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{#all 0}}big{{/all}}', | ||
| 46 | output: '', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{#all ""}}big{{else}}small{{/all}}', | ||
| 50 | output: 'small', | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: '{{#all 0}}big{{else}}small{{/all}}', | ||
| 54 | output: 'small', | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | input: '{{#all num2}}big{{else}}small{{/all}}', | ||
| 58 | output: 'big', | ||
| 59 | }, | ||
| 60 | { | ||
| 61 | input: '{{#all product}}big{{else}}small{{/all}}', | ||
| 62 | output: 'big', | ||
| 63 | }, | ||
| 64 | { | ||
| 65 | input: '{{#all itemArray}}big{{else}}small{{/all}}', | ||
| 66 | output: 'big', | ||
| 67 | }, | ||
| 68 | { | ||
| 69 | input: '{{#all string}}big{{else}}small{{/all}}', | ||
| 70 | output: 'big', | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | input: '{{#all emptyObject}}big{{else}}small{{/all}}', | ||
| 74 | output: 'small', | ||
| 75 | }, | ||
| 76 | ], done); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | it('should render "big" if all conditions truthy', function(done) { | |
| 80 | runTestCases([ | ||
| 81 | { | ||
| 82 | input: '{{#all "1" true}}big{{/all}}', | ||
| 83 | output: 'big', | ||
| 84 | }, | ||
| 85 | { | ||
| 86 | input: '{{#all 1 "1"}}big{{/all}}', | ||
| 87 | output: 'big', | ||
| 88 | }, | ||
| 89 | { | ||
| 90 | input: '{{#all "text" alwaysTrue}}big{{/all}}', | ||
| 91 | output: 'big', | ||
| 92 | }, | ||
| 93 | { | ||
| 94 | input: '{{#all alwaysTrue product num1 num2}}big{{/all}}', | ||
| 95 | output: 'big', | ||
| 96 | }, | ||
| 97 | { | ||
| 98 | input: '{{#all alwaysTrue itemArray string}}big{{/all}}', | ||
| 99 | output: 'big', | ||
| 100 | }, | ||
| 101 | ], done); | ||
| 102 | }); | ||
| 103 | |||
| 104 | 1 | it('should render empty if any condition is falsy', function(done) { | |
| 105 | runTestCases([ | ||
| 106 | { | ||
| 107 | input: '{{#all emptyString num1}}big{{/all}}', | ||
| 108 | output: '', | ||
| 109 | }, | ||
| 110 | { | ||
| 111 | input: '{{#all true 0 emptyArray alwaysTrue}}big{{/all}}', | ||
| 112 | output: '', | ||
| 113 | }, | ||
| 114 | { | ||
| 115 | input: '{{#all true "" alwaysTrue}}big{{/all}}', | ||
| 116 | output: '', | ||
| 117 | }, | ||
| 118 | { | ||
| 119 | input: '{{#all alwaysNull}}big{{/all}}', | ||
| 120 | output: '', | ||
| 121 | }, | ||
| 122 | ], done); | ||
| 123 | }); | ||
| 124 | }); | ||
| 125 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('any helper (with option hash)', function() { | |
| 8 | const context = { | ||
| 9 | big: 'big', | ||
| 10 | arrayWithObjs: [{a: 1},{b: 1},{a: 2}] | ||
| 11 | }; | ||
| 12 | |||
| 13 | // Build a test runner that uses a default context | ||
| 14 | 1 | const runTestCases = testRunner({context}); | |
| 15 | |||
| 16 | 1 | it('should return "big" with matching predicate ', function(done) { | |
| 17 | runTestCases([ | ||
| 18 | { | ||
| 19 | input: '{{#any arrayWithObjs a=1}}{{big}}{{/any}}', | ||
| 20 | output: 'big', | ||
| 21 | }, | ||
| 22 | { | ||
| 23 | input: '{{#any arrayWithObjs a=2}}{{big}}{{/any}}', | ||
| 24 | output: 'big', | ||
| 25 | }, | ||
| 26 | { | ||
| 27 | input: '{{#any arrayWithObjs b=1}}{{big}}{{/any}}', | ||
| 28 | output: 'big', | ||
| 29 | }, | ||
| 30 | ], done); | ||
| 31 | }); | ||
| 32 | |||
| 33 | 1 | it('should return nothing without matching predicate ', function(done) { | |
| 34 | runTestCases([ | ||
| 35 | { | ||
| 36 | input: '{{#any arrayWithObjs b=2}}{{big}}{{/any}}', | ||
| 37 | output: '', | ||
| 38 | }, | ||
| 39 | { | ||
| 40 | input: '{{#any arrayWithObjs c=1}}{{big}}{{/any}}', | ||
| 41 | output: '', | ||
| 42 | }, | ||
| 43 | { | ||
| 44 | input: '{{#any arrayWithObjs num=2}}{{big}}{{/any}}', | ||
| 45 | output: '', | ||
| 46 | }, | ||
| 47 | { | ||
| 48 | input: '{{#any foobar num=2}}{{big}}{{/any}}', | ||
| 49 | output: '', | ||
| 50 | context: { | ||
| 51 | foobar: null | ||
| 52 | } | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | input: '{{#any foobar num=2}}{{big}}{{/any}}', | ||
| 56 | output: '', | ||
| 57 | context: { | ||
| 58 | foobar: undefined | ||
| 59 | } | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | input: '{{#any undefined num=1}}{{big}}{{/any}}', | ||
| 63 | output: '', | ||
| 64 | context: { | ||
| 65 | foobar: undefined | ||
| 66 | } | ||
| 67 | }, | ||
| 68 | { | ||
| 69 | input: '{{#any null num=null}}{{big}}{{/any}}', | ||
| 70 | output: '', | ||
| 71 | }, | ||
| 72 | ], done); | ||
| 73 | }); | ||
| 74 | }); | ||
| 75 | |||
| 76 | |||
| 77 | 1 | describe('any helper (with multiple arguments)', function() { | |
| 78 | // DEPRECATED: Moved to #or helper | ||
| 79 | const context = { | ||
| 80 | num1: 1, | ||
| 81 | num2: 2, | ||
| 82 | product: {a: 1, b: 2}, | ||
| 83 | string: 'yolo', | ||
| 84 | alwaysTrue: true, | ||
| 85 | alwaysFalse: false, | ||
| 86 | emptyArray: [], | ||
| 87 | emptyObject: {}, | ||
| 88 | itemArray: [1,2], | ||
| 89 | big: 'big', | ||
| 90 | arrayWithObjs: [{a: 1},{b: 1},{a: 2}] | ||
| 91 | }; | ||
| 92 | |||
| 93 | // Build a test runner that uses a default context | ||
| 94 | 1 | const runTestCases = testRunner({context}); | |
| 95 | |||
| 96 | 1 | it('should return "big" if at least one arg valid', function(done) { | |
| 97 | runTestCases([ | ||
| 98 | { | ||
| 99 | input: '{{#any arrayWithObjs string}}{{big}}{{/any}}', | ||
| 100 | output: 'big', | ||
| 101 | }, | ||
| 102 | { | ||
| 103 | input: '{{#any "something test" itemArray}}{{big}}{{/any}}', | ||
| 104 | output: 'big', | ||
| 105 | }, | ||
| 106 | { | ||
| 107 | input: '{{#any "this is before the other test"}}{{big}}{{/any}}', | ||
| 108 | output: 'big', | ||
| 109 | }, | ||
| 110 | { | ||
| 111 | input: '{{#any alwaysFalse emptyArray string}}{{big}}{{/any}}', | ||
| 112 | output: 'big', | ||
| 113 | }, | ||
| 114 | ], done); | ||
| 115 | }); | ||
| 116 | |||
| 117 | 1 | it('should return "" when no arguments are valid', function(done) { | |
| 118 | runTestCases([ | ||
| 119 | { | ||
| 120 | input: '{{#any emptyArray emptyObject alwaysFalse}}{{big}}{{/any}}', | ||
| 121 | output: '', | ||
| 122 | }, | ||
| 123 | { | ||
| 124 | input: '{{#any "" false}}{{big}}{{/any}}', | ||
| 125 | output: '', | ||
| 126 | }, | ||
| 127 | { | ||
| 128 | input: '{{#any fooBar}}{{big}}{{/any}}', | ||
| 129 | output: '', | ||
| 130 | context: { | ||
| 131 | fooBar: null | ||
| 132 | } | ||
| 133 | }, | ||
| 134 | { | ||
| 135 | input: '{{#any fooBar}}{{big}}{{/any}}', | ||
| 136 | output: '', | ||
| 137 | context: {} | ||
| 138 | }, | ||
| 139 | { | ||
| 140 | input: '{{#any undefined}}{{big}}{{/any}}', | ||
| 141 | output: '', | ||
| 142 | }, | ||
| 143 | ], done); | ||
| 144 | }); | ||
| 145 | }); | ||
| 146 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | 1 | const AssignVarHelper = require('../../helpers/assignVar')[0]; | |
| 9 | |||
| 10 | 1 | describe('assignVar and getVar helpers', function() { | |
| 11 | |||
| 12 | const generateOverflowString = (length, char) => { | ||
| 13 | return `${[...Array(length).keys()].map(_=>char).join("")}`; | ||
| 14 | } | ||
| 15 | |||
| 16 | 1 | const context = { | |
| 17 | value1: "Big", | ||
| 18 | value2: "Commerce", | ||
| 19 | value3: 12, | ||
| 20 | value4: -12.34, | ||
| 21 | valueUnderStringBuffer: generateOverflowString(AssignVarHelper.max_length-1, "U"), | ||
| 22 | // Bug in testing suite, strings in context that are over or equal to the max length are not being sent to the | ||
| 23 | // helpers. instead an `undefined` is being sent. To handle this we can use a template string in the tests | ||
| 24 | // directly to pretend that the string is being sent to the helpers. | ||
| 25 | valueFillStringBuffer: generateOverflowString(AssignVarHelper.max_length, "F"), | ||
| 26 | valueOverflowStringBufferBy1: generateOverflowString(AssignVarHelper.max_length+1, "O"), | ||
| 27 | valueEmptyString: "", | ||
| 28 | valueNull: null, | ||
| 29 | valueEncodedSafeString: "<script>alert('XSS')</script>", | ||
| 30 | }; | ||
| 31 | |||
| 32 | 1 | const runTestCases = testRunner({context}); | |
| 33 | |||
| 34 | 1 | it('should throw an exception if the assignVar key is not a string', function (done) { | |
| 35 | renderString('{{assignVar 1 2}}').catch(_ => { | ||
| 36 | done(); | ||
| 37 | }); | ||
| 38 | }); | ||
| 39 | |||
| 40 | 1 | it('should throw an exception if the getVar key is not a string', function (done) { | |
| 41 | renderString('{{getVar 2}}').catch(_ => { | ||
| 42 | done(); | ||
| 43 | }); | ||
| 44 | }); | ||
| 45 | |||
| 46 | 1 | it('should assign and get variables', function(done) { | |
| 47 | runTestCases([ | ||
| 48 | { | ||
| 49 | // New test case: Assigning a variable with an empty string value | ||
| 50 | input: "{{assignVar 'data1' valueEmptyString}}{{getVar 'data1'}}", | ||
| 51 | output: '', | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | // New test case: Assigning a variable with a string value, then delete it by assigning null | ||
| 55 | input: "{{assignVar 'data1' value1}}{{getVar 'data1'}} {{assignVar 'data1' null}}{{getVar 'data1'}} {{assignVar 'data1' value1}}{{getVar 'data1'}} {{assignVar 'data1' valueNull}}{{getVar 'data1'}}", | ||
| 56 | output: 'Big Big ', | ||
| 57 | }, | ||
| 58 | { | ||
| 59 | // New test case: Assigning a variable with a string value, then delete it by assigning undefined | ||
| 60 | input: "{{assignVar 'data1' value1}}{{getVar 'data1'}} {{assignVar 'data1' undefined}}{{getVar 'data1'}}", | ||
| 61 | output: 'Big ', | ||
| 62 | }, | ||
| 63 | { | ||
| 64 | input: "{{assignVar 'data1' value1}}{{assignVar 'data2' 12}}{{getVar 'data1'}} {{getVar 'data2'}}", | ||
| 65 | output: 'Big 12', | ||
| 66 | }, | ||
| 67 | { | ||
| 68 | input: "{{assignVar 'data1' value1}}{{assignVar 'data2' value4}}{{getVar 'data1'}} {{getVar 'data2'}}", | ||
| 69 | output: 'Big -12.34', | ||
| 70 | }, | ||
| 71 | { | ||
| 72 | input: "{{assignVar 'data1' value1}}{{assignVar 'data2' value2}}{{getVar 'data1'}}{{getVar 'data2'}}", | ||
| 73 | output: 'BigCommerce', | ||
| 74 | }, | ||
| 75 | { | ||
| 76 | input: "{{assignVar 'data1' null}}{{assignVar 'data2' undefined}}{{getVar 'data1'}}{{getVar 'data2'}}", | ||
| 77 | output: '', | ||
| 78 | }, | ||
| 79 | ], done); | ||
| 80 | }); | ||
| 81 | |||
| 82 | // Check to see if the assignVar still works when the situation is that no data has been stored yet, and a delete instruction is given. | ||
| 83 | 1 | it('should accept null and undefined as input before any variables are stored', function(done) { | |
| 84 | runTestCases([ | ||
| 85 | { | ||
| 86 | input: "{{assignVar 'data1' null}}{{assignVar 'data2' undefined}}{{getVar 'data1'}}{{getVar 'data2'}}", | ||
| 87 | output: '', | ||
| 88 | }, | ||
| 89 | ], done); | ||
| 90 | }); | ||
| 91 | |||
| 92 | 1 | it('should return empty string if variable is not defined', function(done) { | |
| 93 | runTestCases([ | ||
| 94 | { | ||
| 95 | input: "{{getVar 'data3'}}", | ||
| 96 | output: '', | ||
| 97 | }, | ||
| 98 | { | ||
| 99 | input: "{{assignVar 'data1' value1}}{{getVar 'data3'}}", | ||
| 100 | output: '', | ||
| 101 | }, | ||
| 102 | ], done); | ||
| 103 | }); | ||
| 104 | |||
| 105 | 1 | it('should return empty string if variable is not *yet* defined', function(done) { | |
| 106 | runTestCases([ | ||
| 107 | { | ||
| 108 | input: "{{getVar 'data1'}}{{assignVar 'data1' value1}}", | ||
| 109 | output: '', | ||
| 110 | }, | ||
| 111 | ], done); | ||
| 112 | }); | ||
| 113 | |||
| 114 | 1 | it('should return integers with correct type', function(done) { | |
| 115 | runTestCases([ | ||
| 116 | { | ||
| 117 | input: "{{assignVar 'data1' value3}}{{assignVar 'data2' 10}}{{getVar 'data1'}} + {{getVar 'data2'}} = {{add (getVar 'data1') (getVar 'data2')}}", | ||
| 118 | output: '12 + 10 = 22', | ||
| 119 | }, | ||
| 120 | ], done); | ||
| 121 | }); | ||
| 122 | |||
| 123 | 1 | it('should accept a "SafeString" object as a valid alternative to a string (results should be a string when stored)', function(done) { | |
| 124 | runTestCases([ | ||
| 125 | { | ||
| 126 | input: "{{assignVar 'data1' (encodeHtmlEntities valueEncodedSafeString)}}{{getVar 'data1'}}", | ||
| 127 | output: '&#x3C;script&#x3E;alert(&#x27;XSS&#x27;)&#x3C;/script&#x3E;', // SAY NO TO XSS | ||
| 128 | }, | ||
| 129 | ], done); | ||
| 130 | }); | ||
| 131 | |||
| 132 | 1 | it(`should accept a string up to the maximum length of the buffer (Max length: ${AssignVarHelper.max_length})`, function(done) { | |
| 133 | runTestCases([ | ||
| 134 | { | ||
| 135 | input: "{{assignVar 'data1' valueUnderStringBuffer}}{{getVar 'data1'}}", | ||
| 136 | output: `${context.valueUnderStringBuffer}`, | ||
| 137 | }, | ||
| 138 | ], done); | ||
| 139 | }); | ||
| 140 | |||
| 141 | 1 | it(`should throw an error if buffer is filled (Max length: ${AssignVarHelper.max_length})`, | |
| 142 | function(done) { | ||
| 143 | renderString(`{{assignVar "fill" "${context.valueFillStringBuffer}"}}`).catch(_ => { | ||
| 144 | // If inline injection of string is not done, the test will fail when it should pass via the catch. | ||
| 145 | // See context above for more information. | ||
| 146 | done(); | ||
| 147 | }); | ||
| 148 | } | ||
| 149 | ); | ||
| 150 | |||
| 151 | 1 | it(`should throw an error if buffer is overflowed (Max length: ${AssignVarHelper.max_length})`, | |
| 152 | function(done) { | ||
| 153 | renderString(`{{assignVar 'overflow' "${context.valueOverflowStringBufferBy1}"}}`).catch(_ => { | ||
| 154 | // If inline injection of string is not done, the test will fail when it should pass via the catch. | ||
| 155 | // See context above for more information. | ||
| 156 | done(); | ||
| 157 | }); | ||
| 158 | } | ||
| 159 | ); | ||
| 160 | |||
| 161 | 1 | it(`should allow for the writing of up to and including maximum variables (Maximum: ${AssignVarHelper.max_keys})`, function(done) { | |
| 162 | runTestCases([ | ||
| 163 | { | ||
| 164 | input: `{{#for 1 ${AssignVarHelper.max_keys}}}{{assignVar (multiConcat 'data' this.$index) this.$index}}{{getVar (multiConcat 'data' this.$index)}}{{/for}}`, | ||
| 165 | output: [...Array(50).keys()] | ||
| 166 | .map(n=>n+1) // Start at 1 | ||
| 167 | .join(""), | ||
| 168 | }, | ||
| 169 | ], done); | ||
| 170 | }); | ||
| 171 | |||
| 172 | 1 | it(`should fail when creating more than the maximum variables (Overflow at: ${AssignVarHelper.max_keys+1})`, | |
| 173 | function(done) { | ||
| 174 | renderString(`{{#for 1 ${AssignVarHelper.max_keys+1}}}{{assignVar (multiConcat 'data' this.$index) this.$index}}{{getVar (multiConcat 'data' this.$index)}}{{/for}}`, | ||
| 175 | ).catch(_ => { | ||
| 176 | done(); | ||
| 177 | }); | ||
| 178 | } | ||
| 179 | ); | ||
| 180 | |||
| 181 | 1 | it("should allow for more variable assignments when some are deleted", function(done) { | |
| 182 | runTestCases([ | ||
| 183 | { | ||
| 184 | input: ` | ||
| 185 | {{~#for 1 ${AssignVarHelper.max_keys}}}{{assignVar (multiConcat 'data' this.$index) this.$index}}{{getVar (multiConcat 'data' this.$index)}}{{/for}} | ||
| 186 | {{~assignVar 'data1' null}} | ||
| 187 | {{~assignVar 'data2' null}} | ||
| 188 | {{~#for ${AssignVarHelper.max_keys+1} ${AssignVarHelper.max_keys+2}}}{{assignVar (multiConcat 'data' this.$index) this.$index}}{{getVar (multiConcat 'data' this.$index)}}{{/for}} | ||
| 189 | `.trim(), | ||
| 190 | output: [...Array(50).keys()] | ||
| 191 | .map(n=>n+1) // Start at 1 | ||
| 192 | .join("") + "51" + "52", | ||
| 193 | }, | ||
| 194 | ], done); | ||
| 195 | }); | ||
| 196 | |||
| 197 | 1 | it('should return undefined accessing proto/constructor', function(done) { | |
| 198 | runTestCases([ | ||
| 199 | { | ||
| 200 | input: "{{getVar '__proto__'}}", | ||
| 201 | output: '', | ||
| 202 | }, | ||
| 203 | { | ||
| 204 | input: "{{getVar 'constructor'}}", | ||
| 205 | output: '', | ||
| 206 | }, | ||
| 207 | ], done); | ||
| 208 | }); | ||
| 209 | }); | ||
| 210 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const describe = lab.experiment; | |
| 5 | 1 | const expect = Code.expect; | |
| 6 | 1 | const it = lab.it; | |
| 7 | 1 | const { render } = require('../spec-helpers'); | |
| 8 | |||
| 9 | 1 | describe('partial and block helpers', function () { | |
| 10 | it('should insert partial into the corresponding block', function (done) { | ||
| 11 | var templates = { | ||
| 12 | template: '{{#partial "page"}}<h1>{{title}}</h1><p>{{content}}</p>{{/partial}}{{> layout}}', | ||
| 13 | layout: '<html><body>{{#block "page"}}{{/block}}</body>/<html>', | ||
| 14 | }, | ||
| 15 | context = { | ||
| 16 | title: 'Hello', | ||
| 17 | content: 'World', | ||
| 18 | }; | ||
| 19 | |||
| 20 | 1 | render('template', context, {}, {}, templates).then(result => { | |
| 21 | expect(result).to.contain('<html><body><h1>Hello</h1><p>World</p></body>/<html>'); | ||
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | }); | ||
| 25 | |||
| 26 | 1 | it('should not trigger an error if partial name is empty, fallback case used instead', function (done) { | |
| 27 | var templates = { | ||
| 28 | template: '{{#partial }}World{{/partial}}{{> layout}}', | ||
| 29 | layout: 'Hello{{#block "page"}} Brave New World{{/block}}', | ||
| 30 | }; | ||
| 31 | |||
| 32 | 1 | render('template', {}, {}, {}, templates).then(result => { | |
| 33 | expect(result).to.be.equal('Hello Brave New World'); | ||
| 34 | 1 | done(); | |
| 35 | }); | ||
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it('should not trigger an error if block name is empty', function (done) { | |
| 39 | var templates = { | ||
| 40 | template: '{{#partial "page" "2134"}}World{{/partial}}{{> layout}}', | ||
| 41 | layout: 'Hello{{#block}}{{/block}}', | ||
| 42 | }; | ||
| 43 | |||
| 44 | 1 | render('template', {}, {}, {}, templates).then(result => { | |
| 45 | expect(result).to.be.equal('Hello'); | ||
| 46 | 1 | done(); | |
| 47 | }); | ||
| 48 | }); | ||
| 49 | |||
| 50 | 1 | it('should successfully render template', function (done) { | |
| 51 | const templateContent = "some-content"; | ||
| 52 | 1 | const templates = { | |
| 53 | "layout/base": templateContent, | ||
| 54 | template: `{{#JSONparse '{"layout/base":{}}'}}{{#partial this}}{{/partial}}{{/JSONparse}}{{>layout/base}}`, | ||
| 55 | }; | ||
| 56 | 1 | render('template', {}, {}, {}, templates).then(result => { | |
| 57 | expect(result).to.be.equal(templateContent); | ||
| 58 | 1 | done(); | |
| 59 | }); | ||
| 60 | }); | ||
| 61 | |||
| 62 | 1 | it('should successfully render template with context', function (done) { | |
| 63 | const templateContent = "Hello, world!"; | ||
| 64 | 1 | const templates = { | |
| 65 | template: `{{#partial "base"}}Hello, world!{{/partial}}{{#partial notPartials}}{{/partial}}{{> base}}`, | ||
| 66 | }; | ||
| 67 | 1 | const context = { | |
| 68 | notPartials: { | ||
| 69 | "base": {} | ||
| 70 | } | ||
| 71 | } | ||
| 72 | 1 | render('template', context, {}, {}, templates).then(result => { | |
| 73 | expect(result).to.be.equal(templateContent); | ||
| 74 | 1 | done(); | |
| 75 | }); | ||
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('should return empty string if using a reserved object property name', function (done) { | |
| 79 | const templates = { | ||
| 80 | template: '{{#partial "__proto__"}}Page partial content.{{/partial}}{{> layout}}', | ||
| 81 | layout: '{{#block "constructor"}}{{/block}}', | ||
| 82 | }; | ||
| 83 | |||
| 84 | 1 | const context = {}; | |
| 85 | |||
| 86 | 1 | render('template', context, {}, {}, templates).then(result => { | |
| 87 | expect(result).to.equal(''); | ||
| 88 | 1 | done(); | |
| 89 | }); | ||
| 90 | }); | ||
| 91 | }); | ||
| 92 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | {testRunner, buildRenderer} = require('../spec-helpers'); | ||
| 6 | 1 | const {expect} = require("code"); | |
| 7 | |||
| 8 | 1 | describe('cdn helper', function () { | |
| 9 | const context = { | ||
| 10 | brand: 'visa', | ||
| 11 | }; | ||
| 12 | 1 | const siteSettings = { | |
| 13 | cdn_url: 'https://cdn.bcapp/3dsf74g', | ||
| 14 | theme_version_id: '123', | ||
| 15 | }; | ||
| 16 | 1 | const themeSettings = { | |
| 17 | cdn: { | ||
| 18 | customcdn1: 'https://bigcommerce.customcdn.net', | ||
| 19 | customcdn2: 'http://cdn.mystore.com', | ||
| 20 | } | ||
| 21 | }; | ||
| 22 | 1 | const hbVersion = 'v4'; | |
| 23 | |||
| 24 | // Build a test runner that uses a default context | ||
| 25 | 1 | const runTestCases = testRunner({context, siteSettings, themeSettings, hbVersion}); | |
| 26 | |||
| 27 | 1 | it('should render the css cdn url and produce resource hints when attribute resourceHint is part of the template', function (done) { | |
| 28 | const renderer = buildRenderer(siteSettings, themeSettings, {}, hbVersion); | ||
| 29 | 1 | const runTestCases = testRunner({context, renderer}); | |
| 30 | 1 | runTestCases([ | |
| 31 | { | ||
| 32 | input: '{{cdn "assets/css/style.css" resourceHint="preload" as="style" crossorigin="use-credentials"}}', | ||
| 33 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.css', | ||
| 34 | }, | ||
| 35 | { | ||
| 36 | input: '{{cdn "/assets/css/style.modal.css" resourceHint="preconnect" as="style" crossorigin="anonymous"}}', | ||
| 37 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.modal.css', | ||
| 38 | }, | ||
| 39 | { | ||
| 40 | input: '{{cdn "/assets/css/style.modal.css" as="style" crossorigin="use-credentials"}}', | ||
| 41 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.modal.css', | ||
| 42 | } | ||
| 43 | ], () => { | ||
| 44 | const hints = renderer.getResourceHints(); | ||
| 45 | 1 | expect(hints).to.have.length(2); | |
| 46 | 1 | hints.forEach(hint => { | |
| 47 | expect(hint.state).to.satisfy((state) => state === 'preload' || state === 'preconnect'); | ||
| 48 | 2 | expect(hint.type).to.equals("style"); | |
| 49 | 2 | expect(hint.cors).to.satisfy((cors) => cors === 'anonymous' || cors === 'use-credentials'); | |
| 50 | }); | ||
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('should render the css cdn url and produce resource hint without as/type attribute', function (done) { | |
| 56 | const renderer = buildRenderer(siteSettings, themeSettings, {}, hbVersion); | ||
| 57 | 1 | const runTestCases = testRunner({context, renderer}); | |
| 58 | 1 | runTestCases([ | |
| 59 | { | ||
| 60 | input: '{{cdn "assets/css/style.css" resourceHint="preload" as="should-not-be-included"}}', | ||
| 61 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.css', | ||
| 62 | } | ||
| 63 | ], () => { | ||
| 64 | const hints = renderer.getResourceHints(); | ||
| 65 | 1 | expect(hints).to.have.length(1); | |
| 66 | 1 | hints.forEach(hint => { | |
| 67 | expect(hint.state).to.equals('preload'); | ||
| 68 | 1 | expect(hint.type).to.not.exist(); | |
| 69 | 1 | expect(hint.cors).to.equals('no'); | |
| 70 | }); | ||
| 71 | 1 | done(); | |
| 72 | }); | ||
| 73 | }); | ||
| 74 | |||
| 75 | 1 | it('should render the css cdn url', function (done) { | |
| 76 | runTestCases([ | ||
| 77 | { | ||
| 78 | input: '{{cdn "assets/css/style.css"}}', | ||
| 79 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.css', | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | input: '{{cdn "/assets/css/style.css"}}', | ||
| 83 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/css/style.css', | ||
| 84 | }, | ||
| 85 | ], done); | ||
| 86 | }); | ||
| 87 | |||
| 88 | 1 | it('should render normal assets cdn url', function (done) { | |
| 89 | runTestCases([ | ||
| 90 | { | ||
| 91 | input: '{{cdn "assets/js/app.js"}}', | ||
| 92 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/js/app.js', | ||
| 93 | }, | ||
| 94 | { | ||
| 95 | input: '{{cdn "assets/img/image.jpg"}}', | ||
| 96 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/image.jpg', | ||
| 97 | }, | ||
| 98 | ], done); | ||
| 99 | }); | ||
| 100 | |||
| 101 | 1 | it('should not use the cdn url', function (done) { | |
| 102 | runTestCases([ | ||
| 103 | { | ||
| 104 | input: '{{cdn "assets/img/image.jpg"}}', | ||
| 105 | output: '/assets/img/image.jpg', | ||
| 106 | siteSettings: {}, | ||
| 107 | themeSettings: {}, | ||
| 108 | }, | ||
| 109 | { | ||
| 110 | input: '{{cdn "assets/img/image.jpg"}}', | ||
| 111 | output: '/assets/img/image.jpg', | ||
| 112 | siteSettings: {}, | ||
| 113 | themeSettings: {}, | ||
| 114 | }, | ||
| 115 | ], done); | ||
| 116 | }); | ||
| 117 | |||
| 118 | 1 | it('should return the same value if it is a full url', function (done) { | |
| 119 | runTestCases([ | ||
| 120 | { | ||
| 121 | input: '{{cdn "https://example.com/app.js"}}', | ||
| 122 | output: 'https://example.com/app.js', | ||
| 123 | }, | ||
| 124 | { | ||
| 125 | input: '{{cdn "http://example.com/app.js"}}', | ||
| 126 | output: 'http://example.com/app.js', | ||
| 127 | }, | ||
| 128 | { | ||
| 129 | input: '{{cdn "//example.com/app.js"}}', | ||
| 130 | output: '//example.com/app.js', | ||
| 131 | }, | ||
| 132 | ], done); | ||
| 133 | }); | ||
| 134 | |||
| 135 | 1 | it('should delete any . and / from the begining of the path if this is not a full path', function (done) { | |
| 136 | runTestCases([ | ||
| 137 | { | ||
| 138 | input: '{{cdn "../img/icon-sprite.svg"}}', | ||
| 139 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 140 | }, | ||
| 141 | { | ||
| 142 | input: '{{cdn "./img/icon-sprite.svg"}}', | ||
| 143 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 144 | }, | ||
| 145 | { | ||
| 146 | input: '{{cdn "/img/icon-sprite.svg"}}', | ||
| 147 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 148 | }, | ||
| 149 | { | ||
| 150 | input: '{{cdn "../../img/icon-sprite.svg"}}', | ||
| 151 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 152 | }, | ||
| 153 | { | ||
| 154 | input: '{{cdn "../../../img/icon-sprite.svg"}}', | ||
| 155 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 156 | }, | ||
| 157 | { | ||
| 158 | input: '{{cdn "../../../IMG/icon-sprite.svg"}}', | ||
| 159 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/IMG/icon-sprite.svg', | ||
| 160 | }, | ||
| 161 | { | ||
| 162 | input: '{{cdn "../../../456IMG/icon-sprite.svg"}}', | ||
| 163 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg', | ||
| 164 | }, | ||
| 165 | { | ||
| 166 | input: '{{cdn "../../../456IMG/icon-sprite.svg/"}}', | ||
| 167 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg/', | ||
| 168 | }, | ||
| 169 | { | ||
| 170 | input: '{{cdn "../../../456IMG/icon-sprite.svg."}}', | ||
| 171 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg.', | ||
| 172 | }, | ||
| 173 | { | ||
| 174 | input: '{{cdn "../../../456IMG/icon-sprite.svg./"}}', | ||
| 175 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg./', | ||
| 176 | }, | ||
| 177 | { | ||
| 178 | input: '{{cdn "../../../456IMG/icon-sprite.svg../"}}', | ||
| 179 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg../', | ||
| 180 | }, | ||
| 181 | { | ||
| 182 | input: '{{cdn "../../../456IMG/icon-sprite.svg../../"}}', | ||
| 183 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/456IMG/icon-sprite.svg../../', | ||
| 184 | }, | ||
| 185 | ], done); | ||
| 186 | }); | ||
| 187 | |||
| 188 | 1 | it('should return an empty string if no path is provided', function (done) { | |
| 189 | runTestCases([ | ||
| 190 | { | ||
| 191 | input: '{{cdn ""}}', | ||
| 192 | output: '', | ||
| 193 | }, | ||
| 194 | ], done); | ||
| 195 | }); | ||
| 196 | |||
| 197 | 1 | it('should return a webDav asset if webdav protocol specified', function (done) { | |
| 198 | runTestCases([ | ||
| 199 | { | ||
| 200 | input: '{{cdn "webdav:img/image.jpg"}}', | ||
| 201 | output: 'https://cdn.bcapp/3dsf74g/content/img/image.jpg', | ||
| 202 | }, | ||
| 203 | { | ||
| 204 | input: '{{cdn "webdav:/img/image.jpg"}}', | ||
| 205 | output: 'https://cdn.bcapp/3dsf74g/content/img/image.jpg', | ||
| 206 | }, | ||
| 207 | { | ||
| 208 | input: '{{cdn "webdav://img/image.jpg"}}', | ||
| 209 | output: 'https://cdn.bcapp/3dsf74g/content/img/image.jpg', | ||
| 210 | }, | ||
| 211 | ], done); | ||
| 212 | }); | ||
| 213 | |||
| 214 | 1 | it('should not return a webDav asset if webdav protocol is not correct', function (done) { | |
| 215 | runTestCases([ | ||
| 216 | { | ||
| 217 | input: '{{cdn "webbav:img/image.jpg"}}', | ||
| 218 | output: '/img/image.jpg', | ||
| 219 | }, | ||
| 220 | { | ||
| 221 | input: '{{cdn "webbav:/img/image.jpg"}}', | ||
| 222 | output: '/img/image.jpg', | ||
| 223 | }, | ||
| 224 | ], done); | ||
| 225 | }); | ||
| 226 | |||
| 227 | 1 | it('should return a custom CDN asset if protocol is configured', function (done) { | |
| 228 | runTestCases([ | ||
| 229 | { | ||
| 230 | input: '{{cdn "customcdn1:img/image.jpg"}}', | ||
| 231 | output: 'https://bigcommerce.customcdn.net/img/image.jpg', | ||
| 232 | }, | ||
| 233 | { | ||
| 234 | input: '{{cdn "customcdn1:/img/image.jpg"}}', | ||
| 235 | output: 'https://bigcommerce.customcdn.net/img/image.jpg', | ||
| 236 | }, | ||
| 237 | { | ||
| 238 | input: '{{cdn "customcdn2:img/image.jpg"}}', | ||
| 239 | output: 'http://cdn.mystore.com/img/image.jpg', | ||
| 240 | }, | ||
| 241 | { | ||
| 242 | input: '{{cdn "customcdn2:/img/image.jpg"}}', | ||
| 243 | output: 'http://cdn.mystore.com/img/image.jpg', | ||
| 244 | }, | ||
| 245 | ], done); | ||
| 246 | }); | ||
| 247 | |||
| 248 | 1 | it('should return a custom CDN asset when using nested helper', function (done) { | |
| 249 | runTestCases([ | ||
| 250 | { | ||
| 251 | input: '{{cdn (concat "customcdn1:" "img/image.jpg")}}', | ||
| 252 | output: 'https://bigcommerce.customcdn.net/img/image.jpg', | ||
| 253 | }, | ||
| 254 | { | ||
| 255 | input: '{{cdn (concat "customcdn1:" "/img/image.jpg")}}', | ||
| 256 | output: 'https://bigcommerce.customcdn.net/img/image.jpg', | ||
| 257 | }, | ||
| 258 | { | ||
| 259 | input: '{{cdn (concat "customcdn2:" "img/image.jpg")}}', | ||
| 260 | output: 'http://cdn.mystore.com/img/image.jpg', | ||
| 261 | }, | ||
| 262 | { | ||
| 263 | input: '{{cdn (concat "customcdn2:" "/img/image.jpg")}}', | ||
| 264 | output: 'http://cdn.mystore.com/img/image.jpg', | ||
| 265 | }, | ||
| 266 | ], done); | ||
| 267 | }); | ||
| 268 | |||
| 269 | 1 | it('should return a custom CDN asset when using nested concat helper', function (done) { | |
| 270 | runTestCases([ | ||
| 271 | { | ||
| 272 | input: '{{cdn (concat (concat "customcdn2:" "/img/image.jpg") "?test")}}', | ||
| 273 | output: 'http://cdn.mystore.com/img/image.jpg?test', | ||
| 274 | }, | ||
| 275 | ], done); | ||
| 276 | }); | ||
| 277 | |||
| 278 | 1 | it('should return a local CDN asset if no cdn url is configured', function (done) { | |
| 279 | runTestCases([ | ||
| 280 | { | ||
| 281 | input: '{{cdn "customcdn1:img/image.jpg"}}', | ||
| 282 | output: '/assets/cdn/customcdn1/img/image.jpg', | ||
| 283 | siteSettings: {}, | ||
| 284 | }, | ||
| 285 | { | ||
| 286 | input: '{{cdn "customcdn1:/img/image.jpg"}}', | ||
| 287 | output: '/assets/cdn/customcdn1/img/image.jpg', | ||
| 288 | siteSettings: {}, | ||
| 289 | }, | ||
| 290 | { | ||
| 291 | input: '{{cdn "customcdn2:img/image.jpg"}}', | ||
| 292 | output: '/assets/cdn/customcdn2/img/image.jpg', | ||
| 293 | siteSettings: {}, | ||
| 294 | }, | ||
| 295 | { | ||
| 296 | input: '{{cdn "customcdn2:/img/image.jpg"}}', | ||
| 297 | output: '/assets/cdn/customcdn2/img/image.jpg', | ||
| 298 | siteSettings: {}, | ||
| 299 | }, | ||
| 300 | { | ||
| 301 | input: "{{cdn (concat (concat 'img/payment-methods/' brand) '.svg')}}", | ||
| 302 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/payment-methods/visa.svg', | ||
| 303 | } | ||
| 304 | ], done); | ||
| 305 | }); | ||
| 306 | |||
| 307 | 1 | it('should not return a custom CDN asset if protocol is not configured', function (done) { | |
| 308 | runTestCases([ | ||
| 309 | { | ||
| 310 | input: '{{cdn "customcdn1:img/image.jpg"}}', | ||
| 311 | output: '/img/image.jpg', | ||
| 312 | themeSettings: {}, | ||
| 313 | }, | ||
| 314 | { | ||
| 315 | input: '{{cdn "customcdn1:/img/image.jpg"}}', | ||
| 316 | output: '/img/image.jpg', | ||
| 317 | themeSettings: {}, | ||
| 318 | }, | ||
| 319 | ], done); | ||
| 320 | }); | ||
| 321 | |||
| 322 | 1 | it('should return basic asset URL if protocol is not correct', function (done) { | |
| 323 | runTestCases([ | ||
| 324 | { | ||
| 325 | input: '{{cdn "randomProtocol::img/image.jpg"}}', | ||
| 326 | output: '/img/image.jpg', | ||
| 327 | }, | ||
| 328 | { | ||
| 329 | input: '{{cdn "randomProtocol:/img/image.jpg"}}', | ||
| 330 | output: '/img/image.jpg', | ||
| 331 | }, | ||
| 332 | ], done); | ||
| 333 | }); | ||
| 334 | |||
| 335 | 1 | it('should avoid double slash in path', function (done) { | |
| 336 | runTestCases([ | ||
| 337 | { | ||
| 338 | input: '{{cdn "img/icon-sprite.svg"}}', | ||
| 339 | output: 'https://cdn.bcapp/3dsf74g/stencil/123/img/icon-sprite.svg', | ||
| 340 | } | ||
| 341 | ], done); | ||
| 342 | }); | ||
| 343 | }); | ||
| 344 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('compare helper', function() { | |
| 8 | const context = { | ||
| 9 | num1: 1, | ||
| 10 | num2: 2, | ||
| 11 | product: {a: 1, b: 2}, | ||
| 12 | string: 'yolo' | ||
| 13 | }; | ||
| 14 | |||
| 15 | // Build a test runner that uses a default context | ||
| 16 | 1 | const runTestCases = testRunner({context}); | |
| 17 | |||
| 18 | 1 | it('should render "big" if all compares match', function(done) { | |
| 19 | runTestCases([ | ||
| 20 | { | ||
| 21 | input: '{{#compare "1" num1 operator="=="}}big{{/compare}}', | ||
| 22 | output: 'big', | ||
| 23 | }, | ||
| 24 | { | ||
| 25 | input: '{{#compare 1 num1 operator="==="}}big{{/compare}}', | ||
| 26 | output: 'big', | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | input: '{{#compare 2 num1 operator="!=="}}big{{/compare}}', | ||
| 30 | output: 'big', | ||
| 31 | }, | ||
| 32 | { | ||
| 33 | input: '{{#compare num2 num1 operator="!="}}big{{/compare}}', | ||
| 34 | output: 'big', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: '{{#compare num2 num1 operator=">"}}big{{/compare}}', | ||
| 38 | output: 'big', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{#compare num1 num2 operator="<"}}big{{/compare}}', | ||
| 42 | output: 'big', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{#compare num2 num1 operator=">="}}big{{/compare}}', | ||
| 46 | output: 'big', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{#compare num1 num2 operator="<="}}big{{/compare}}', | ||
| 50 | output: 'big', | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: '{{#compare product "object" operator="typeof"}}big{{/compare}}', | ||
| 54 | output: 'big', | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | input: '{{#compare string "string" operator="typeof"}}big{{/compare}}', | ||
| 58 | output: 'big', | ||
| 59 | }, | ||
| 60 | ], done); | ||
| 61 | }); | ||
| 62 | |||
| 63 | 1 | it('should render empty for all cases', function(done) { | |
| 64 | runTestCases([ | ||
| 65 | { | ||
| 66 | input: '{{#compare "2" num1 operator="=="}}big{{/compare}}', | ||
| 67 | output: '', | ||
| 68 | }, | ||
| 69 | { | ||
| 70 | input: '{{#compare 2 num1 operator="==="}}big{{/compare}}', | ||
| 71 | output: '', | ||
| 72 | }, | ||
| 73 | { | ||
| 74 | input: '{{#compare 1 num1 operator="!=="}}big{{/compare}}', | ||
| 75 | output: '', | ||
| 76 | }, | ||
| 77 | { | ||
| 78 | input: '{{#compare num2 2 operator="!="}}big{{/compare}}', | ||
| 79 | output: '', | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | input: '{{#compare num1 20 operator=">"}}big{{/compare}}', | ||
| 83 | output: '', | ||
| 84 | }, | ||
| 85 | { | ||
| 86 | input: '{{#compare 4 num2 operator="<"}}big{{/compare}}', | ||
| 87 | output: '', | ||
| 88 | }, | ||
| 89 | { | ||
| 90 | input: '{{#compare num1 40 operator=">="}}big{{/compare}}', | ||
| 91 | output: '', | ||
| 92 | }, | ||
| 93 | { | ||
| 94 | input: '{{#compare num2 num1 operator="<="}}big{{/compare}}', | ||
| 95 | output: '', | ||
| 96 | }, | ||
| 97 | { | ||
| 98 | input: '{{#compare product "string" operator="typeof"}}big{{/compare}}', | ||
| 99 | output: '', | ||
| 100 | }, | ||
| 101 | { | ||
| 102 | input: '{{#compare string "object" operator="typeof"}}big{{/compare}}', | ||
| 103 | output: '', | ||
| 104 | }, | ||
| 105 | ], done); | ||
| 106 | }); | ||
| 107 | }); | ||
| 108 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('concat helper', function() { | |
| 8 | const context = { | ||
| 9 | var1: 'hello', | ||
| 10 | var2: 'world', | ||
| 11 | }; | ||
| 12 | |||
| 13 | // Build a test runner | ||
| 14 | 1 | const runTestCases = testRunner({context}); | |
| 15 | |||
| 16 | 1 | it('should concatanate two strings', function(done) { | |
| 17 | runTestCases([ | ||
| 18 | { | ||
| 19 | input: '{{concat var1 var2}}', | ||
| 20 | output: 'helloworld', | ||
| 21 | }, | ||
| 22 | ], done); | ||
| 23 | }); | ||
| 24 | }); | ||
| 25 | |||
| 26 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('decrementVar helper', function() { | |
| 11 | const context = { | ||
| 12 | value1: 12 | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | const runTestCases = testRunner({context}); | |
| 16 | |||
| 17 | 1 | it('should throw an exception if the decrementVar key is not a string', function (done) { | |
| 18 | renderString('{{decrementVar 1}}').catch(e => { | ||
| 19 | done(); | ||
| 20 | }); | ||
| 21 | }); | ||
| 22 | |||
| 23 | 1 | it('should correctly decrement', function(done) { | |
| 24 | runTestCases([ | ||
| 25 | { | ||
| 26 | input: "{{decrementVar 'data1'}}{{getVar 'data1'}} {{decrementVar 'data1'}}{{getVar 'data1'}} {{decrementVar 'data1'}}{{getVar 'data1'}}", | ||
| 27 | output: '00 -1-1 -2-2', | ||
| 28 | }, | ||
| 29 | ], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should correctly decrement an existing variable', function(done) { | |
| 33 | runTestCases([ | ||
| 34 | { | ||
| 35 | input: "{{assignVar 'data1' value1}}{{getVar 'data1'}} {{decrementVar 'data1'}}", | ||
| 36 | output: '12 11', | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | input: "{{assignVar 'data1' 12}}{{getVar 'data1'}} {{decrementVar 'data1'}}", | ||
| 40 | output: '12 11', | ||
| 41 | }, | ||
| 42 | { | ||
| 43 | input: "{{assignVar 'data1' -12}}{{getVar 'data1'}} {{decrementVar 'data1'}}", | ||
| 44 | output: '-12 -13', | ||
| 45 | }, | ||
| 46 | ], done); | ||
| 47 | }); | ||
| 48 | |||
| 49 | 1 | it('should correctly overwrite an existing non-integer variable', function(done) { | |
| 50 | runTestCases([ | ||
| 51 | { | ||
| 52 | input: "{{assignVar 'data1' 'a'}}{{getVar 'data1'}} {{decrementVar 'data1'}}", | ||
| 53 | output: 'a 0', | ||
| 54 | }, | ||
| 55 | ], done); | ||
| 56 | }); | ||
| 57 | |||
| 58 | 1 | it('should correctly return data accession object proto/constructor', function(done) { | |
| 59 | runTestCases([ | ||
| 60 | { | ||
| 61 | input: "{{decrementVar '__proto__'}}", | ||
| 62 | output: '0', | ||
| 63 | }, | ||
| 64 | { | ||
| 65 | input: "{{decrementVar 'constructor'}}", | ||
| 66 | output: '0', | ||
| 67 | }, | ||
| 68 | ], done); | ||
| 69 | }); | ||
| 70 | }); | ||
| 71 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const path = require('path'); | |
| 2 | 1 | const Lab = require('lab'), | |
| 3 | lab = exports.lab = Lab.script(), | ||
| 4 | describe = lab.experiment, | ||
| 5 | it = lab.it, | ||
| 6 | testRunner = require('../spec-helpers').testRunner; | ||
| 7 | |||
| 8 | 1 | describe('dynamicComponent helper', function() { | |
| 9 | const templates = { | ||
| 10 | [path.normalize('component/fields/one')]: '1{{animal}}', | ||
| 11 | [path.normalize('component/fields/two')]: '2{{animal}}', | ||
| 12 | [path.normalize('component/fields/three')]: '3{{animal}}', | ||
| 13 | }, | ||
| 14 | context = { | ||
| 15 | fields: [ | ||
| 16 | {partial: 'one', animal: 'dog'}, | ||
| 17 | {partial: 'two', animal: 'cat'}, | ||
| 18 | {partial: 'three', animal: 'mouse'}, | ||
| 19 | ], | ||
| 20 | field: {partial: 'two', animal: 'elephant'} | ||
| 21 | }; | ||
| 22 | |||
| 23 | // Build a test runner | ||
| 24 | 1 | const runTestCases = testRunner({context, templates}); | |
| 25 | |||
| 26 | 1 | it('should render all dynamic templates accordingly', function(done) { | |
| 27 | runTestCases([ | ||
| 28 | { | ||
| 29 | input: '{{#each fields}}{{dynamicComponent "component/fields"}} {{/each}}', | ||
| 30 | output: '1dog 2cat 3mouse ', | ||
| 31 | }, | ||
| 32 | ], done); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return undefined when accessing proto/constructor', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{#each fields}}{{dynamicComponent "__proto__"}}{{/each}}', | ||
| 39 | output: '', | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{#each fields}}{{dynamicComponent "constructor"}}{{/each}}', | ||
| 43 | output: '', | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | }); | ||
| 48 | |||
| 49 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | beforeEach = lab.beforeEach; | ||
| 6 | 1 | const {buildRenderer, testRunner} = require("../spec-helpers"); | |
| 7 | 1 | const {expect} = require("code"); | |
| 8 | 1 | const { | |
| 9 | resourceHintAllowedTypes, | ||
| 10 | resourceHintAllowedStates, | ||
| 11 | resourceHintAllowedCors | ||
| 12 | } = require("../../helpers/lib/resourceHints"); | ||
| 13 | |||
| 14 | function template(href, rel, type, cors) { | ||
| 15 | 2 | cors = cors ? `crossorigin="${cors}"` : ''; | |
| 16 | 2 | type = type ? `as="${type}"` : ''; | |
| 17 | 2 | return `{{ earlyHint "${href}" "${rel}" ${type} ${cors} }}`; | |
| 18 | } | ||
| 19 | |||
| 20 | function randommer(items) { | ||
| 21 | 3 | return () => items[Math.floor(Math.random() * items.length)]; | |
| 22 | } | ||
| 23 | |||
| 24 | 1 | const randomHintState = randommer(Object.values(resourceHintAllowedStates)); | |
| 25 | 1 | const randomHintType = randommer(Object.values(resourceHintAllowedTypes)); | |
| 26 | 1 | const randomCors = randommer(Object.values(resourceHintAllowedCors)); | |
| 27 | |||
| 28 | 1 | let renderer, runTests; | |
| 29 | |||
| 30 | 1 | describe('earlyHint', () => { | |
| 31 | |||
| 32 | beforeEach(done => { | ||
| 33 | renderer = buildRenderer(); | ||
| 34 | 2 | runTests = testRunner({renderer}); | |
| 35 | 2 | done(); | |
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it('should create a resource hint with only required properties', done => { | |
| 39 | const path = '/asset/theme.css' | ||
| 40 | 1 | const state = randomHintState(); | |
| 41 | 1 | const input = template(path, state); | |
| 42 | 1 | runTests([ | |
| 43 | { | ||
| 44 | input, | ||
| 45 | output: path | ||
| 46 | } | ||
| 47 | ], () => { | ||
| 48 | const hints = renderer.getResourceHints(); | ||
| 49 | 1 | expect(hints).to.have.length(1); | |
| 50 | 1 | expect(hints[0]).to.equals({src: path, state, cors: 'no'}); | |
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('should create a resource hint with all the properties', done => { | |
| 56 | const path = '/asset/theme.css' | ||
| 57 | 1 | const state = randomHintState(); | |
| 58 | 1 | const type = randomHintType(); | |
| 59 | 1 | const cors = randomCors(); | |
| 60 | 1 | const input = template(path, state, type, cors); | |
| 61 | 1 | runTests([ | |
| 62 | { | ||
| 63 | input, | ||
| 64 | output: path | ||
| 65 | } | ||
| 66 | ], () => { | ||
| 67 | const hints = renderer.getResourceHints(); | ||
| 68 | 1 | expect(hints).to.have.length(1); | |
| 69 | 1 | expect(hints[0]).to.equals({src: path, state, type, cors}); | |
| 70 | 1 | done(); | |
| 71 | }); | ||
| 72 | }); | ||
| 73 | |||
| 74 | }); | ||
| 75 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('encodeHtmlEntities helper', function() { | |
| 11 | const context = { | ||
| 12 | string: 'foo © bar ≠ baz 𝌆 qux', | ||
| 13 | quotes: '"some" \'quotes\'', | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | const runTestCases = testRunner({context}); | |
| 17 | |||
| 18 | // Some test cases lifted from https://github.com/mathiasbynens/he | ||
| 19 | |||
| 20 | 1 | it('should return a string with HTML entities encoded', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux"}}', | ||
| 24 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 25 | }, | ||
| 26 | { | ||
| 27 | input: '{{encodeHtmlEntities string}}', | ||
| 28 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | input: '{{encodeHtmlEntities "string"}}', | ||
| 32 | output: `string`, | ||
| 33 | }, | ||
| 34 | { | ||
| 35 | input: '{{encodeHtmlEntities quotes}}', | ||
| 36 | output: `"some" 'quotes'`, | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | input: '{{encodeHtmlEntities "an ampersand: &"}}', | ||
| 40 | output: `an ampersand: &`, | ||
| 41 | }, | ||
| 42 | ], done); | ||
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('should return a string with HTML entities encoded with named references', function(done) { | |
| 46 | runTestCases([ | ||
| 47 | { | ||
| 48 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" useNamedReferences="true"}}', | ||
| 49 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 50 | }, | ||
| 51 | ], done); | ||
| 52 | }); | ||
| 53 | |||
| 54 | 1 | it('should return a string with HTML entities encoded with decimal option', function(done) { | |
| 55 | runTestCases([ | ||
| 56 | { | ||
| 57 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" decimal="true"}}', | ||
| 58 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 59 | }, | ||
| 60 | ], done); | ||
| 61 | }); | ||
| 62 | |||
| 63 | 1 | it('should return a string with HTML entities encoded with named references and decimal option', function(done) { | |
| 64 | runTestCases([ | ||
| 65 | { | ||
| 66 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" useNamedReferences="true" decimal="true"}}', | ||
| 67 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 68 | }, | ||
| 69 | ], done); | ||
| 70 | }); | ||
| 71 | |||
| 72 | 1 | it('should return a string with HTML entities encoded with encodeEverything option', function(done) { | |
| 73 | runTestCases([ | ||
| 74 | { | ||
| 75 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" encodeEverything="true"}}', | ||
| 76 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 77 | }, | ||
| 78 | ], done); | ||
| 79 | }); | ||
| 80 | |||
| 81 | 1 | it('should return a string with HTML entities encoded with encodeEverything and useNamedReferences option', function(done) { | |
| 82 | runTestCases([ | ||
| 83 | { | ||
| 84 | input: '{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" encodeEverything="true" useNamedReferences="true"}}', | ||
| 85 | output: `foo © bar ≠ baz 𝌆 qux`, | ||
| 86 | }, | ||
| 87 | ], done); | ||
| 88 | }); | ||
| 89 | |||
| 90 | 1 | it('should return a string with HTML entities encoded with allowUnsafeSymbols option', function(done) { | |
| 91 | runTestCases([ | ||
| 92 | { | ||
| 93 | input: '{{encodeHtmlEntities "foo © and & ampersand" allowUnsafeSymbols="true"}}', | ||
| 94 | output: `foo © and & ampersand`, | ||
| 95 | }, | ||
| 96 | ], done); | ||
| 97 | }); | ||
| 98 | |||
| 99 | 1 | it('should throw an exception if an invalid named argument is passed', function (done) { | |
| 100 | renderString('{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" useNamedReferences="blah"}}').catch(e => { | ||
| 101 | renderString('{{encodeHtmlEntities "foo © bar ≠ baz 𝌆 qux" blah="true"}}').catch(e => { | ||
| 102 | done(); | ||
| 103 | }); | ||
| 104 | }); | ||
| 105 | }); | ||
| 106 | |||
| 107 | 1 | it('should throw an exception if a non-string argument is passed', function (done) { | |
| 108 | renderString('{{encodeHtmlEntities 123}}').catch(e => { | ||
| 109 | done(); | ||
| 110 | }); | ||
| 111 | }); | ||
| 112 | }); | ||
| 113 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('equals helper', function() { | |
| 8 | const context = { | ||
| 9 | value: 5 | ||
| 10 | }; | ||
| 11 | |||
| 12 | // Build a test runner | ||
| 13 | 1 | const runTestCases = testRunner({context}); | |
| 14 | |||
| 15 | 1 | it('should render yes if the value is equal to 5', function(done) { | |
| 16 | runTestCases([ | ||
| 17 | { | ||
| 18 | input: '{{#equals 5 value}}yes{{/equals}}', | ||
| 19 | output: 'yes', | ||
| 20 | }, | ||
| 21 | { | ||
| 22 | input: '{{#equals value 5}}yes{{/equals}}', | ||
| 23 | output: 'yes', | ||
| 24 | }, | ||
| 25 | ], done); | ||
| 26 | }); | ||
| 27 | |||
| 28 | 1 | it('should render empty string', function(done) { | |
| 29 | runTestCases([ | ||
| 30 | { | ||
| 31 | input: '{{#equals 6 value}}yes{{/equals}}', | ||
| 32 | output: '', | ||
| 33 | }, | ||
| 34 | { | ||
| 35 | input: '{{#equals value 6}}yes{{/equals}}', | ||
| 36 | output: '', | ||
| 37 | }, | ||
| 38 | ], done); | ||
| 39 | }); | ||
| 40 | }); | ||
| 41 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('for helper', function() { | |
| 8 | const context = { name: 'Joe' }; | ||
| 9 | |||
| 10 | // Build a test runner | ||
| 11 | 1 | const runTestCases = testRunner({context}); | |
| 12 | |||
| 13 | 1 | it('should iterate once.', function(done) { | |
| 14 | runTestCases([ | ||
| 15 | { | ||
| 16 | input: '{{#for 1 this}}{{$index}}:{{name}} {{/for}}', | ||
| 17 | output: '1:Joe ', | ||
| 18 | }, | ||
| 19 | { | ||
| 20 | input: '{{#for 1 1 this}}{{$index}}:{{name}} {{/for}}', | ||
| 21 | output: '1:Joe ', | ||
| 22 | }, | ||
| 23 | { | ||
| 24 | input: '{{#for 0 0 this}}{{$index}}:{{name}} {{/for}}', | ||
| 25 | output: '0:Joe ', | ||
| 26 | }, | ||
| 27 | { | ||
| 28 | input: '{{#for 1000 1000 this}}{{$index}}:{{name}} {{/for}}', | ||
| 29 | output: '1000:Joe ', | ||
| 30 | }, | ||
| 31 | ], done); | ||
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('should iterate 10 times', function(done) { | |
| 35 | runTestCases([ | ||
| 36 | { | ||
| 37 | input: '{{#for 10 this}}{{$index}}:{{name}} {{/for}}', | ||
| 38 | output: '1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe 10:Joe ', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{#for 1 10 this}}{{$index}}:{{name}} {{/for}}', | ||
| 42 | output: '1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe 10:Joe ', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{#for 0 9 this}}{{$index}}:{{name}} {{/for}}', | ||
| 46 | output: '0:Joe 1:Joe 2:Joe 3:Joe 4:Joe 5:Joe 6:Joe 7:Joe 8:Joe 9:Joe ', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{#for 1000 1010 this}}{{$index}}:{{name}} {{/for}}', | ||
| 50 | output: '1000:Joe 1001:Joe 1002:Joe 1003:Joe 1004:Joe 1005:Joe 1006:Joe 1007:Joe 1008:Joe 1009:Joe 1010:Joe ', | ||
| 51 | }, | ||
| 52 | ], done); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('should not iterate more than 100 times', function(done) { | |
| 56 | const expected = '.'.repeat(100); | ||
| 57 | 1 | runTestCases([ | |
| 58 | { | ||
| 59 | input: '{{#for 0 99}}.{{/for}}', | ||
| 60 | output: expected, | ||
| 61 | }, | ||
| 62 | { | ||
| 63 | input: '{{#for 1 100}}.{{/for}}', | ||
| 64 | output: expected, | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | input: '{{#for 0 3000 this}}.{{/for}}', | ||
| 68 | output: expected, | ||
| 69 | }, | ||
| 70 | { | ||
| 71 | input: '{{#for 2015 3000}}.{{/for}}', | ||
| 72 | output: expected, | ||
| 73 | }, | ||
| 74 | ], done); | ||
| 75 | }); | ||
| 76 | |||
| 77 | 1 | it('should render w/o context', function(done) { | |
| 78 | runTestCases([ | ||
| 79 | { | ||
| 80 | input: '{{#for 10}}{{$index}} {{/for}}', | ||
| 81 | output: '1 2 3 4 5 6 7 8 9 10 ', | ||
| 82 | }, | ||
| 83 | { | ||
| 84 | input: '{{#for 1 10}}{{$index}} {{/for}}', | ||
| 85 | output: '1 2 3 4 5 6 7 8 9 10 ', | ||
| 86 | }, | ||
| 87 | { | ||
| 88 | input: '{{#for 0 9}}{{$index}} {{/for}}', | ||
| 89 | output: '0 1 2 3 4 5 6 7 8 9 ', | ||
| 90 | }, | ||
| 91 | { | ||
| 92 | input: '{{#for 0 20}}.{{/for}}', | ||
| 93 | output: '.....................', | ||
| 94 | }, | ||
| 95 | { | ||
| 96 | input: '{{#for 0 99}}{{/for}}', | ||
| 97 | output: '', | ||
| 98 | }, | ||
| 99 | ], done); | ||
| 100 | }); | ||
| 101 | |||
| 102 | 1 | it('should convert strings to integers and iterate 10 times', function(done) { | |
| 103 | const context = { | ||
| 104 | start: '1', | ||
| 105 | end: '10' | ||
| 106 | }; | ||
| 107 | |||
| 108 | 1 | runTestCases([ | |
| 109 | { | ||
| 110 | input: '{{#for start end}}{{$index}} {{/for}}', | ||
| 111 | output: '1 2 3 4 5 6 7 8 9 10 ', | ||
| 112 | context: context, | ||
| 113 | }, | ||
| 114 | ], done); | ||
| 115 | }); | ||
| 116 | |||
| 117 | 1 | it('should not iterate if "from" is less than "to"', function(done) { | |
| 118 | const context = { | ||
| 119 | start: 10, | ||
| 120 | end: 1 | ||
| 121 | }; | ||
| 122 | |||
| 123 | 1 | runTestCases([ | |
| 124 | { | ||
| 125 | input: '{{#for start end}}{{$index}} {{/for}}', | ||
| 126 | output: '', | ||
| 127 | context: context, | ||
| 128 | }, | ||
| 129 | ], done); | ||
| 130 | }); | ||
| 131 | }); | ||
| 132 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('get helper', function () { | |
| 8 | const context = { | ||
| 9 | array: [1, 2, 3, 4, 5], | ||
| 10 | options: { a: { b: { c: 'd' } } }, | ||
| 11 | aa: 'a', | ||
| 12 | ab: 'b', | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | const runTestCases = testRunner({ context }); | |
| 16 | |||
| 17 | 1 | it('gets a nested prop', (done) => { | |
| 18 | runTestCases([ | ||
| 19 | { | ||
| 20 | input: `{{get "a.b.c" this.options}}`, | ||
| 21 | output: `d`, | ||
| 22 | } | ||
| 23 | ], done); | ||
| 24 | }); | ||
| 25 | |||
| 26 | 1 | it('does not access prototype properties', (done) => { | |
| 27 | context.__proto__ = {x: 'yz'}; | ||
| 28 | 1 | runTestCases([ | |
| 29 | { | ||
| 30 | input: `{{get "x" this}}`, | ||
| 31 | output: ``, | ||
| 32 | } | ||
| 33 | ], done); | ||
| 34 | }); | ||
| 35 | |||
| 36 | 1 | it('accepts SafeString paths', (done) => { | |
| 37 | runTestCases([ | ||
| 38 | { | ||
| 39 | input: `{{get (concat 'a' 'a') this}}`, | ||
| 40 | output: `a`, | ||
| 41 | }, | ||
| 42 | { | ||
| 43 | input: `{{get (concat 'a' 'b') this}}`, | ||
| 44 | output: `b`, | ||
| 45 | }, | ||
| 46 | { | ||
| 47 | input: `{{get (concat 'options.a' '.b.c') this}}`, | ||
| 48 | output: `d`, | ||
| 49 | } | ||
| 50 | ], done); | ||
| 51 | }); | ||
| 52 | |||
| 53 | 1 | it('gets context from options object if none was given', (done) => { | |
| 54 | runTestCases([ | ||
| 55 | { | ||
| 56 | input: `{{#get 'hash.a' a='some string'}}{{this}}{{/get}}`, | ||
| 57 | output: `some string`, | ||
| 58 | }, | ||
| 59 | ], done); | ||
| 60 | }); | ||
| 61 | |||
| 62 | 1 | it('renders to the empty string if no args are passed', (done) => { | |
| 63 | runTestCases([ | ||
| 64 | { | ||
| 65 | input: `{{get }}`, | ||
| 66 | output: ``, | ||
| 67 | } | ||
| 68 | ], done); | ||
| 69 | }); | ||
| 70 | |||
| 71 | 1 | it('returns the object arg if it is a string, number, undefined, or null', (done) => { | |
| 72 | runTestCases([ | ||
| 73 | { | ||
| 74 | input: `{{get 'a' 'some string'}}`, | ||
| 75 | output: `some string`, | ||
| 76 | }, | ||
| 77 | { | ||
| 78 | input: `{{get 'a' 42}}`, | ||
| 79 | output: `42`, | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | input: `{{get 'a' undefined}}`, | ||
| 83 | output: ``, | ||
| 84 | }, | ||
| 85 | { | ||
| 86 | input: `{{get 'a' null}}`, | ||
| 87 | output: ``, | ||
| 88 | } | ||
| 89 | ], done); | ||
| 90 | }); | ||
| 91 | |||
| 92 | 1 | it('returns undefined if prop path does not exist', (done) => { | |
| 93 | runTestCases([ | ||
| 94 | { // a key does not exist | ||
| 95 | input: `{{get 'z.z' options}}`, | ||
| 96 | output: ``, | ||
| 97 | }, | ||
| 98 | { // first key exists, but its value is `undefined` | ||
| 99 | input:`{{get '0.zyx' (pluck (arrayify options) 'b')}}`, // array: [ undefined ] | ||
| 100 | output: ``, | ||
| 101 | } | ||
| 102 | ], done); | ||
| 103 | }); | ||
| 104 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('getContentImage helper', function() { | |
| 11 | const siteSettings = { | ||
| 12 | cdn_url: 'https://cdn.bcapp/3dsf74g', | ||
| 13 | }; | ||
| 14 | 1 | const context = { | |
| 15 | object: {a:1} | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context, siteSettings}); | |
| 19 | |||
| 20 | |||
| 21 | 1 | it('should throw an exception if a url is passed', function (done) { | |
| 22 | renderString('{{getContentImage "http://example.com/image.jpg"}}').catch(e => { | ||
| 23 | done(); | ||
| 24 | }); | ||
| 25 | }); | ||
| 26 | |||
| 27 | 1 | it('should throw an exception if a non-string is passed', function (done) { | |
| 28 | renderString('{{getContentImage object}}').catch(e => { | ||
| 29 | renderString('{{getContentImage 123').catch(e => { | ||
| 30 | done(); | ||
| 31 | }); | ||
| 32 | }); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return an original image if no size is passed', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{getContentImage "asset.jpg"}}', | ||
| 39 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{getContentImage "folder/asset.jpg"}}', | ||
| 43 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/folder/asset.jpg' | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should return an original image if invalid sizes are passed', function(done) { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{getContentImage "asset.jpg" width="a"}}', | ||
| 52 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | input: '{{getContentImage "asset.jpg" width="a" height="a"}}', | ||
| 56 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 57 | }, | ||
| 58 | { | ||
| 59 | input: '{{getContentImage "asset.jpg" height=123}}', | ||
| 60 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 61 | }, | ||
| 62 | { | ||
| 63 | input: '{{getContentImage "folder/asset.jpg" height=123}}', | ||
| 64 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/folder/asset.jpg' | ||
| 65 | }, | ||
| 66 | ], done); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | it('should return a sized image', function(done) { | |
| 70 | runTestCases([ | ||
| 71 | { | ||
| 72 | input: '{{getContentImage "asset.jpg" width=123}}', | ||
| 73 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/content/asset.jpg' | ||
| 74 | }, | ||
| 75 | { | ||
| 76 | input: '{{getContentImage "folder/asset.jpg" width=123}}', | ||
| 77 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/content/folder/asset.jpg' | ||
| 78 | }, | ||
| 79 | { | ||
| 80 | input: '{{getContentImage "asset.jpg" width=123 height=321}}', | ||
| 81 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/content/asset.jpg' | ||
| 82 | }, | ||
| 83 | { | ||
| 84 | input: '{{getContentImage "folder/asset.jpg" width=123 height=321}}', | ||
| 85 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/content/folder/asset.jpg' | ||
| 86 | }, | ||
| 87 | ], done); | ||
| 88 | }); | ||
| 89 | |||
| 90 | 1 | it('should support lossy compression parameter', function(done) { | |
| 91 | runTestCases([ | ||
| 92 | { | ||
| 93 | input: '{{getContentImage "asset.jpg" lossy=true}}', | ||
| 94 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg?compression=lossy' | ||
| 95 | }, | ||
| 96 | { | ||
| 97 | input: '{{getContentImage "asset.jpg" width=123 lossy=true}}', | ||
| 98 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/content/asset.jpg?compression=lossy' | ||
| 99 | }, | ||
| 100 | { | ||
| 101 | input: '{{getContentImage "asset.jpg" width=123 height=321 lossy=true}}', | ||
| 102 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/content/asset.jpg?compression=lossy' | ||
| 103 | }, | ||
| 104 | { | ||
| 105 | input: '{{getContentImage "asset.jpg" lossy=false}}', | ||
| 106 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 107 | }, | ||
| 108 | { | ||
| 109 | input: '{{getContentImage "asset.jpg"}}', | ||
| 110 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/content/asset.jpg' | ||
| 111 | }, | ||
| 112 | ], done); | ||
| 113 | }); | ||
| 114 | }); | ||
| 115 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('getContentImageSrcset helper', function() { | |
| 11 | const siteSettings = { | ||
| 12 | cdn_url: 'https://cdn.bcapp/3dsf74g', | ||
| 13 | }; | ||
| 14 | 1 | const context = { | |
| 15 | object: {a:1} | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context, siteSettings}); | |
| 19 | |||
| 20 | |||
| 21 | 1 | it('should throw an exception if a url is passed', function (done) { | |
| 22 | renderString('{{getContentImageSrcset "http://example.com/image.jpg"}}').catch(e => { | ||
| 23 | done(); | ||
| 24 | }); | ||
| 25 | }); | ||
| 26 | |||
| 27 | 1 | it('should throw an exception if a non-string is passed', function (done) { | |
| 28 | renderString('{{getContentImageSrcset object}}').catch(e => { | ||
| 29 | renderString('{{getContentImageSrcset 123').catch(e => { | ||
| 30 | done(); | ||
| 31 | }); | ||
| 32 | }); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return a valid srcset', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{getContentImageSrcset "asset.jpg"}}', | ||
| 39 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/content/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/content/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/content/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/content/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/content/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/content/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/content/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/content/asset.jpg 2560w' | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{getContentImageSrcset "folder/asset.jpg" width=123}}', | ||
| 43 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/content/folder/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/content/folder/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/content/folder/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/content/folder/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/content/folder/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/content/folder/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/content/folder/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/content/folder/asset.jpg 2560w' | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should support lossy compression parameter', function(done) { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{getContentImageSrcset "asset.jpg" lossy=true}}', | ||
| 52 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/content/asset.jpg?compression=lossy 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/content/asset.jpg?compression=lossy 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/content/asset.jpg?compression=lossy 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/content/asset.jpg?compression=lossy 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/content/asset.jpg?compression=lossy 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/content/asset.jpg?compression=lossy 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/content/asset.jpg?compression=lossy 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/content/asset.jpg?compression=lossy 2560w' | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | input: '{{getContentImageSrcset "asset.jpg" lossy=false}}', | ||
| 56 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/content/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/content/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/content/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/content/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/content/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/content/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/content/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/content/asset.jpg 2560w' | ||
| 57 | }, | ||
| 58 | { | ||
| 59 | input: '{{getContentImageSrcset "asset.jpg"}}', | ||
| 60 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/content/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/content/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/content/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/content/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/content/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/content/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/content/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/content/asset.jpg 2560w' | ||
| 61 | }, | ||
| 62 | ], done); | ||
| 63 | }); | ||
| 64 | }); | ||
| 65 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('getFontLoaderConfig', function () { | |
| 8 | it('should return the expected font config', function (done) { | ||
| 9 | const themeSettings = { | ||
| 10 | 'test1-font': 'Google_Open+Sans', | ||
| 11 | 'test2-font': 'Google_Open+Sans_400italic', | ||
| 12 | 'test3-font': 'Google_Open+Sans_700', | ||
| 13 | 'test4-font': 'Google_Karla_700', | ||
| 14 | 'test5-font': 'Google_Lora_400_sans', | ||
| 15 | 'test6-font': 'Google_Volkron', | ||
| 16 | 'test7-font': 'Google_Droid_400,700', | ||
| 17 | 'test8-font': 'Google_Crimson+Text_400,700_sans', | ||
| 18 | 'random-property': 'not a font' | ||
| 19 | }; | ||
| 20 | |||
| 21 | 1 | const expectedConfig = { | |
| 22 | google: { | ||
| 23 | families: ['Open Sans:,400italic,700', 'Karla:700', 'Lora:400', 'Volkron:', 'Droid:400,700', 'Crimson Text:400,700'] | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | 1 | const runTestCases = testRunner({themeSettings}); | |
| 28 | 1 | runTestCases([ | |
| 29 | { | ||
| 30 | input: '{{getFontLoaderConfig}}', | ||
| 31 | output: JSON.stringify(expectedConfig), | ||
| 32 | }, | ||
| 33 | ], done); | ||
| 34 | }); | ||
| 35 | }); | ||
| 36 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | {testRunner, buildRenderer} = require('../spec-helpers'); | ||
| 6 | 1 | const {expect} = require("code"); | |
| 7 | 1 | const {resourceHintAllowedTypes, resourceHintAllowedStates} = require('../../helpers/lib/resourceHints'); | |
| 8 | |||
| 9 | 1 | describe('getFontsCollection', function () { | |
| 10 | it('should return a font link with fonts from theme settings and &display=swap when no font-display value is passed', function (done) { | ||
| 11 | const themeSettings = { | ||
| 12 | 'test1-font': 'Google_Open+Sans', | ||
| 13 | 'test2-font': 'Google_Open+Sans_400italic', | ||
| 14 | 'test3-font': 'Google_Open+Sans_700', | ||
| 15 | 'test4-font': 'Google_Karla_700', | ||
| 16 | 'test5-font': 'Google_Lora_400_sans', | ||
| 17 | 'test6-font': 'Google_Volkron', | ||
| 18 | 'test7-font': 'Google_Droid_400,700', | ||
| 19 | 'test8-font': 'Google_Crimson+Text_400,700_sans', | ||
| 20 | 'random-property': 'not a font' | ||
| 21 | }; | ||
| 22 | 1 | const renderer = buildRenderer({}, themeSettings); | |
| 23 | 1 | const runTestCases = testRunner({renderer}); | |
| 24 | 1 | const href = "https://fonts.googleapis.com/css?family=Open+Sans:,400italic,700%7CKarla:700%7CLora:400%7CVolkron:%7CDroid:400,700%7CCrimson+Text:400,700&display=swap"; | |
| 25 | 1 | runTestCases([ | |
| 26 | { | ||
| 27 | input: '{{getFontsCollection}}', | ||
| 28 | output: `<link href="${href}" rel="stylesheet">`, | ||
| 29 | }, | ||
| 30 | ], () => { | ||
| 31 | const hints = renderer.getResourceHints(); | ||
| 32 | 1 | expect(hints).to.have.length(1); | |
| 33 | 1 | expect(hints[0].src).to.equals(href); | |
| 34 | 1 | expect(hints[0].state).to.equals(resourceHintAllowedStates.preloadResourceHintState); | |
| 35 | 1 | expect(hints[0].type).to.equals(resourceHintAllowedTypes.resourceHintStyleType); | |
| 36 | 1 | done(); | |
| 37 | }); | ||
| 38 | }); | ||
| 39 | 1 | it('should return a font link with fonts from theme settings and &display=swap when font-display value passed is invalid', function (done) { | |
| 40 | const themeSettings = { | ||
| 41 | 'test1-font': 'Google_Open+Sans', | ||
| 42 | 'test2-font': 'Google_Open+Sans_400italic', | ||
| 43 | 'test3-font': 'Google_Open+Sans_700', | ||
| 44 | 'test4-font': 'Google_Karla_700', | ||
| 45 | 'test5-font': 'Google_Lora_400_sans', | ||
| 46 | 'test6-font': 'Google_Volkron', | ||
| 47 | 'test7-font': 'Google_Droid_400,700', | ||
| 48 | 'test8-font': 'Google_Crimson+Text_400,700_sans', | ||
| 49 | 'random-property': 'not a font' | ||
| 50 | }; | ||
| 51 | |||
| 52 | 1 | const runTestCases = testRunner({themeSettings}); | |
| 53 | 1 | runTestCases([ | |
| 54 | { | ||
| 55 | input: '{{getFontsCollection font-display="oreo"}}', | ||
| 56 | output: '<link href="https://fonts.googleapis.com/css?family=Open+Sans:,400italic,700%7CKarla:700%7CLora:400%7CVolkron:%7CDroid:400,700%7CCrimson+Text:400,700&display=swap" rel="stylesheet">', | ||
| 57 | }, | ||
| 58 | ], done); | ||
| 59 | }); | ||
| 60 | 1 | it('should return a font link with fonts from theme settings and &display=${font-display} when valid font-display value is passed', function (done) { | |
| 61 | const themeSettings = { | ||
| 62 | 'test1-font': 'Google_Open+Sans', | ||
| 63 | 'test2-font': 'Google_Open+Sans_400italic', | ||
| 64 | 'test3-font': 'Google_Open+Sans_700', | ||
| 65 | 'test4-font': 'Google_Karla_700', | ||
| 66 | 'test5-font': 'Google_Lora_400_sans', | ||
| 67 | 'test6-font': 'Google_Volkron', | ||
| 68 | 'test7-font': 'Google_Droid_400,700', | ||
| 69 | 'test8-font': 'Google_Crimson+Text_400,700_sans', | ||
| 70 | 'random-property': 'not a font' | ||
| 71 | }; | ||
| 72 | |||
| 73 | 1 | const runTestCases = testRunner({themeSettings}); | |
| 74 | 1 | runTestCases([ | |
| 75 | { | ||
| 76 | input: '{{getFontsCollection font-display="fallback"}}', | ||
| 77 | output: '<link href="https://fonts.googleapis.com/css?family=Open+Sans:,400italic,700%7CKarla:700%7CLora:400%7CVolkron:%7CDroid:400,700%7CCrimson+Text:400,700&display=fallback" rel="stylesheet">', | ||
| 78 | }, | ||
| 79 | ], done); | ||
| 80 | }); | ||
| 81 | 1 | it('should not crash if a malformed Google font is passed when no font-display value is passed', function (done) { | |
| 82 | const themeSettings = { | ||
| 83 | 'test1-font': 'Google_Open+Sans', | ||
| 84 | 'test2-font': 'Google_', | ||
| 85 | 'test3-font': 'Google' | ||
| 86 | }; | ||
| 87 | 1 | const renderer = buildRenderer({}, themeSettings); | |
| 88 | 1 | const runTestCases = testRunner({renderer}); | |
| 89 | 1 | runTestCases([ | |
| 90 | { | ||
| 91 | input: '{{getFontsCollection}}', | ||
| 92 | output: '<link href="https://fonts.googleapis.com/css?family=Open+Sans:&display=swap" rel="stylesheet">', | ||
| 93 | }, | ||
| 94 | ], () => { | ||
| 95 | const hints = renderer.getResourceHints(); | ||
| 96 | 1 | expect(hints).to.have.length(1); | |
| 97 | 1 | expect(hints[0].state).to.equals(resourceHintAllowedStates.preloadResourceHintState); | |
| 98 | 1 | done(); | |
| 99 | }); | ||
| 100 | }); | ||
| 101 | 1 | it('should not crash if a malformed Google font is passed when valid font-display value is passed', function (done) { | |
| 102 | const themeSettings = { | ||
| 103 | 'test1-font': 'Google_Open+Sans', | ||
| 104 | 'test2-font': 'Google_', | ||
| 105 | 'test3-font': 'Google' | ||
| 106 | }; | ||
| 107 | |||
| 108 | 1 | const runTestCases = testRunner({themeSettings}); | |
| 109 | 1 | runTestCases([ | |
| 110 | { | ||
| 111 | input: '{{getFontsCollection font-display="fallback"}}', | ||
| 112 | output: '<link href="https://fonts.googleapis.com/css?family=Open+Sans:&display=fallback" rel="stylesheet">', | ||
| 113 | }, | ||
| 114 | ], done); | ||
| 115 | }); | ||
| 116 | 1 | it('should not crash if a malformed Google font is passed when invalid font-display value is passed', function (done) { | |
| 117 | const themeSettings = { | ||
| 118 | 'test1-font': 'Google_Open+Sans', | ||
| 119 | 'test2-font': 'Google_', | ||
| 120 | 'test3-font': 'Google' | ||
| 121 | }; | ||
| 122 | |||
| 123 | 1 | const runTestCases = testRunner({themeSettings}); | |
| 124 | 1 | runTestCases([ | |
| 125 | { | ||
| 126 | input: '{{getFontsCollection font-display="oreo"}}', | ||
| 127 | output: '<link href="https://fonts.googleapis.com/css?family=Open+Sans:&display=swap" rel="stylesheet">', | ||
| 128 | }, | ||
| 129 | ], done); | ||
| 130 | }); | ||
| 131 | }); | ||
| 132 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | const themeSettings = { | |
| 8 | logo_image: '600x300', | ||
| 9 | gallery: '100x100', | ||
| 10 | _images: { | ||
| 11 | logo: { | ||
| 12 | width: 250, | ||
| 13 | height: 100 | ||
| 14 | }, | ||
| 15 | gallery: { | ||
| 16 | width: 300, | ||
| 17 | height: 300 | ||
| 18 | }, | ||
| 19 | missing_values: {}, | ||
| 20 | missing_width: {height: 100} | ||
| 21 | } | ||
| 22 | }; | ||
| 23 | |||
| 24 | 1 | describe('getImage helper', function() { | |
| 25 | const urlData = 'https://cdn.example.com/path/to/{:size}/image.png?c=2'; | ||
| 26 | 1 | const urlData_2_qs = 'https://cdn.example.com/path/to/{:size}/image.png?c=2&imbypass=on'; | |
| 27 | 1 | const context = { | |
| 28 | image_url: 'http://example.com/image.png', | ||
| 29 | not_an_image: null, | ||
| 30 | image: { | ||
| 31 | data: urlData, | ||
| 32 | width: null, | ||
| 33 | height: null, | ||
| 34 | }, | ||
| 35 | image_with_dimensions: { | ||
| 36 | data: urlData, | ||
| 37 | width: 123, | ||
| 38 | height: 123, | ||
| 39 | }, | ||
| 40 | image_with_2_qs: { | ||
| 41 | data: urlData_2_qs | ||
| 42 | }, | ||
| 43 | logoPreset: 'logo', | ||
| 44 | }; | ||
| 45 | |||
| 46 | 1 | const runTestCases = testRunner({context, themeSettings}); | |
| 47 | |||
| 48 | 1 | it('should return a url if a url is passed', function(done) { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{getImage "http://example.com/image.jpg"}}', | ||
| 52 | output: 'http://example.com/image.jpg', | ||
| 53 | }, | ||
| 54 | ], done); | ||
| 55 | }); | ||
| 56 | |||
| 57 | 1 | it('should return empty if image is invalid', function(done) { | |
| 58 | runTestCases([ | ||
| 59 | { | ||
| 60 | input: '{{getImage not_existing_image}}', | ||
| 61 | output: '', | ||
| 62 | }, | ||
| 63 | { | ||
| 64 | input: '{{getImage not_an_image}}', | ||
| 65 | output: '', | ||
| 66 | }, | ||
| 67 | ], done); | ||
| 68 | }); | ||
| 69 | |||
| 70 | 1 | it('should use the preset from _images', function(done) { | |
| 71 | runTestCases([ | ||
| 72 | { | ||
| 73 | input: '{{getImage image "logo"}}', | ||
| 74 | output: urlData.replace('{:size}', '250x100'), | ||
| 75 | }, | ||
| 76 | { | ||
| 77 | input: '{{getImage image_with_2_qs "logo"}}', | ||
| 78 | output: urlData_2_qs.replace('{:size}', '250x100'), | ||
| 79 | }, | ||
| 80 | { | ||
| 81 | input: '{{getImage image "gallery"}}', | ||
| 82 | output: urlData.replace('{:size}', '300x300'), | ||
| 83 | }, | ||
| 84 | { | ||
| 85 | input: '{{getImage image_with_2_qs "gallery"}}', | ||
| 86 | output: urlData_2_qs.replace('{:size}', '300x300'), | ||
| 87 | }, | ||
| 88 | ], done); | ||
| 89 | }); | ||
| 90 | |||
| 91 | 1 | it('should use the size from the theme_settings', function(done) { | |
| 92 | runTestCases([ | ||
| 93 | { | ||
| 94 | input: '{{getImage image "logo_image"}}', | ||
| 95 | output: urlData.replace('{:size}', '600x300'), | ||
| 96 | }, | ||
| 97 | ], done); | ||
| 98 | }); | ||
| 99 | |||
| 100 | 1 | it('should use the default image url if image is invalid', function(done) { | |
| 101 | runTestCases([ | ||
| 102 | { | ||
| 103 | input: '{{getImage not_an_image "logo" "http://image"}}', | ||
| 104 | output: 'http://image', | ||
| 105 | }, | ||
| 106 | { | ||
| 107 | input: '{{getImage not_an_image "logo" image_url}}', | ||
| 108 | output: context.image_url, | ||
| 109 | }, | ||
| 110 | ], done); | ||
| 111 | }); | ||
| 112 | |||
| 113 | 1 | it('should use original size if not default is passed', function(done) { | |
| 114 | runTestCases([ | ||
| 115 | { | ||
| 116 | input: '{{getImage image "bad_preset"}}', | ||
| 117 | output: urlData.replace('{:size}', 'original'), | ||
| 118 | }, | ||
| 119 | ], done); | ||
| 120 | }); | ||
| 121 | |||
| 122 | 1 | it('should default to max value (width & height) if value is not provided', function(done) { | |
| 123 | runTestCases([ | ||
| 124 | { | ||
| 125 | input: '{{getImage image "missing_values"}}', | ||
| 126 | output: urlData.replace('{:size}', '5120x5120'), | ||
| 127 | }, | ||
| 128 | { | ||
| 129 | input: '{{getImage image "missing_width"}}', | ||
| 130 | output: urlData.replace('{:size}', '5120x100'), | ||
| 131 | }, | ||
| 132 | ], done); | ||
| 133 | }); | ||
| 134 | |||
| 135 | 1 | it('should default to size of the image dimensions if known and a larger size is requested', function(done) { | |
| 136 | runTestCases([ | ||
| 137 | { | ||
| 138 | input: '{{getImage image_with_dimensions "logo"}}', | ||
| 139 | output: urlData.replace('{:size}', '123x100'), | ||
| 140 | }, | ||
| 141 | { | ||
| 142 | input: '{{getImage image_with_dimensions "logo_image"}}', | ||
| 143 | output: urlData.replace('{:size}', '123x123'), | ||
| 144 | }, | ||
| 145 | ], done); | ||
| 146 | }); | ||
| 147 | |||
| 148 | 1 | it('should support lossy compression parameter', function(done) { | |
| 149 | runTestCases([ | ||
| 150 | { | ||
| 151 | input: '{{getImage image "logo" lossy=true}}', | ||
| 152 | output: urlData.replace('{:size}', '250x100') + '&compression=lossy', | ||
| 153 | }, | ||
| 154 | { | ||
| 155 | input: '{{getImage image "logo" lossy=false}}', | ||
| 156 | output: urlData.replace('{:size}', '250x100'), | ||
| 157 | }, | ||
| 158 | { | ||
| 159 | input: '{{getImage image "logo"}}', | ||
| 160 | output: urlData.replace('{:size}', '250x100'), | ||
| 161 | }, | ||
| 162 | { | ||
| 163 | input: '{{getImage image_with_2_qs "logo" lossy=true}}', | ||
| 164 | output: urlData_2_qs.replace('{:size}', '250x100') + '&compression=lossy', | ||
| 165 | }, | ||
| 166 | ], done); | ||
| 167 | }); | ||
| 168 | }); | ||
| 169 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('getImageManagerImage helper', function() { | |
| 11 | const siteSettings = { | ||
| 12 | cdn_url: 'https://cdn.bcapp/3dsf74g', | ||
| 13 | }; | ||
| 14 | 1 | const context = { | |
| 15 | object: {a:1} | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context, siteSettings}); | |
| 19 | |||
| 20 | |||
| 21 | 1 | it('should throw an exception if a url is passed', function (done) { | |
| 22 | renderString('{{getImageManagerImage "http://example.com/image.jpg"}}').catch(e => { | ||
| 23 | done(); | ||
| 24 | }); | ||
| 25 | }); | ||
| 26 | |||
| 27 | 1 | it('should throw an exception if a non-string is passed', function (done) { | |
| 28 | renderString('{{getImageManagerImage object}}').catch(e => { | ||
| 29 | renderString('{{getImageManagerImage 123').catch(e => { | ||
| 30 | done(); | ||
| 31 | }); | ||
| 32 | }); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return an original image if no size is passed', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{getImageManagerImage "asset.jpg"}}', | ||
| 39 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{getImageManagerImage "folder/asset.jpg"}}', | ||
| 43 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/folder/asset.jpg' | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should return an original image if invalid sizes are passed', function(done) { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{getImageManagerImage "asset.jpg" width="a"}}', | ||
| 52 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | input: '{{getImageManagerImage "asset.jpg" width="a" height="a"}}', | ||
| 56 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 57 | }, | ||
| 58 | { | ||
| 59 | input: '{{getImageManagerImage "asset.jpg" height=123}}', | ||
| 60 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 61 | }, | ||
| 62 | { | ||
| 63 | input: '{{getImageManagerImage "folder/asset.jpg" height=123}}', | ||
| 64 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/folder/asset.jpg' | ||
| 65 | }, | ||
| 66 | ], done); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | it('should return a sized image', function(done) { | |
| 70 | runTestCases([ | ||
| 71 | { | ||
| 72 | input: '{{getImageManagerImage "asset.jpg" width=123}}', | ||
| 73 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/image-manager/asset.jpg' | ||
| 74 | }, | ||
| 75 | { | ||
| 76 | input: '{{getImageManagerImage "folder/asset.jpg" width=123}}', | ||
| 77 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/image-manager/folder/asset.jpg' | ||
| 78 | }, | ||
| 79 | { | ||
| 80 | input: '{{getImageManagerImage "asset.jpg" width=123 height=321}}', | ||
| 81 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/image-manager/asset.jpg' | ||
| 82 | }, | ||
| 83 | { | ||
| 84 | input: '{{getImageManagerImage "folder/asset.jpg" width=123 height=321}}', | ||
| 85 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/image-manager/folder/asset.jpg' | ||
| 86 | }, | ||
| 87 | ], done); | ||
| 88 | }); | ||
| 89 | |||
| 90 | 1 | it('should support lossy compression parameter', function(done) { | |
| 91 | runTestCases([ | ||
| 92 | { | ||
| 93 | input: '{{getImageManagerImage "asset.jpg" lossy=true}}', | ||
| 94 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg?compression=lossy' | ||
| 95 | }, | ||
| 96 | { | ||
| 97 | input: '{{getImageManagerImage "asset.jpg" width=123 lossy=true}}', | ||
| 98 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123w/image-manager/asset.jpg?compression=lossy' | ||
| 99 | }, | ||
| 100 | { | ||
| 101 | input: '{{getImageManagerImage "asset.jpg" width=123 height=321 lossy=true}}', | ||
| 102 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/123x321/image-manager/asset.jpg?compression=lossy' | ||
| 103 | }, | ||
| 104 | { | ||
| 105 | input: '{{getImageManagerImage "asset.jpg" lossy=false}}', | ||
| 106 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 107 | }, | ||
| 108 | { | ||
| 109 | input: '{{getImageManagerImage "asset.jpg"}}', | ||
| 110 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/original/image-manager/asset.jpg' | ||
| 111 | }, | ||
| 112 | ], done); | ||
| 113 | }); | ||
| 114 | }); | ||
| 115 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('getImageManagerImageSrcset helper', function() { | |
| 11 | const siteSettings = { | ||
| 12 | cdn_url: 'https://cdn.bcapp/3dsf74g', | ||
| 13 | }; | ||
| 14 | 1 | const context = { | |
| 15 | object: {a:1} | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context, siteSettings}); | |
| 19 | |||
| 20 | |||
| 21 | 1 | it('should throw an exception if a url is passed', function (done) { | |
| 22 | renderString('{{getImageManagerImageSrcset "http://example.com/image.jpg"}}').catch(e => { | ||
| 23 | done(); | ||
| 24 | }); | ||
| 25 | }); | ||
| 26 | |||
| 27 | 1 | it('should throw an exception if a non-string is passed', function (done) { | |
| 28 | renderString('{{getImageManagerImageSrcset object}}').catch(e => { | ||
| 29 | renderString('{{getImageManagerImageSrcset 123').catch(e => { | ||
| 30 | done(); | ||
| 31 | }); | ||
| 32 | }); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return a valid srcset', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{getImageManagerImageSrcset "asset.jpg"}}', | ||
| 39 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/image-manager/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/image-manager/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/image-manager/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/image-manager/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/image-manager/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/image-manager/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/image-manager/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/image-manager/asset.jpg 2560w' | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{getImageManagerImageSrcset "folder/asset.jpg" width=123}}', | ||
| 43 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/image-manager/folder/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/image-manager/folder/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/image-manager/folder/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/image-manager/folder/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/image-manager/folder/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/image-manager/folder/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/image-manager/folder/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/image-manager/folder/asset.jpg 2560w' | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should support lossy compression parameter', function(done) { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{getImageManagerImageSrcset "asset.jpg" lossy=true}}', | ||
| 52 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/image-manager/asset.jpg?compression=lossy 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/image-manager/asset.jpg?compression=lossy 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/image-manager/asset.jpg?compression=lossy 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/image-manager/asset.jpg?compression=lossy 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/image-manager/asset.jpg?compression=lossy 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/image-manager/asset.jpg?compression=lossy 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/image-manager/asset.jpg?compression=lossy 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/image-manager/asset.jpg?compression=lossy 2560w' | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | input: '{{getImageManagerImageSrcset "asset.jpg" lossy=false}}', | ||
| 56 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/image-manager/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/image-manager/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/image-manager/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/image-manager/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/image-manager/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/image-manager/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/image-manager/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/image-manager/asset.jpg 2560w' | ||
| 57 | }, | ||
| 58 | { | ||
| 59 | input: '{{getImageManagerImageSrcset "asset.jpg"}}', | ||
| 60 | output: 'https://cdn.bcapp/3dsf74g/images/stencil/80w/image-manager/asset.jpg 80w, https://cdn.bcapp/3dsf74g/images/stencil/160w/image-manager/asset.jpg 160w, https://cdn.bcapp/3dsf74g/images/stencil/320w/image-manager/asset.jpg 320w, https://cdn.bcapp/3dsf74g/images/stencil/640w/image-manager/asset.jpg 640w, https://cdn.bcapp/3dsf74g/images/stencil/960w/image-manager/asset.jpg 960w, https://cdn.bcapp/3dsf74g/images/stencil/1280w/image-manager/asset.jpg 1280w, https://cdn.bcapp/3dsf74g/images/stencil/1920w/image-manager/asset.jpg 1920w, https://cdn.bcapp/3dsf74g/images/stencil/2560w/image-manager/asset.jpg 2560w' | ||
| 61 | }, | ||
| 62 | ], done); | ||
| 63 | }); | ||
| 64 | }); | ||
| 65 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('getImageSrcset helper', function() { | |
| 8 | const urlData = 'https://cdn.example.com/path/to/{:size}/image.png?c=2'; | ||
| 9 | 1 | const urlData_2_qs = 'https://cdn.example.com/path/to/{:size}/image.png?c=2&imbypass=on'; | |
| 10 | 1 | const context = { | |
| 11 | image_url: 'http://example.com/image.png', | ||
| 12 | not_an_image: null, | ||
| 13 | image: { | ||
| 14 | data: urlData | ||
| 15 | }, | ||
| 16 | image_with_2_qs: { | ||
| 17 | data: urlData_2_qs, | ||
| 18 | width: null, | ||
| 19 | height: null, | ||
| 20 | }, | ||
| 21 | image_with_dimensions: { | ||
| 22 | data: urlData, | ||
| 23 | width: 1400, | ||
| 24 | height: 900, | ||
| 25 | }, | ||
| 26 | }; | ||
| 27 | |||
| 28 | 1 | const runTestCases = testRunner({context}); | |
| 29 | |||
| 30 | 1 | it('should return a srcset if a valid image and srcset sizes are passed', function(done) { | |
| 31 | runTestCases([ | ||
| 32 | { | ||
| 33 | input: '{{getImageSrcset image 100w="100w" 200w="200w" 300w="300w" 1000w="1000w"}}', | ||
| 34 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2 100w, https://cdn.example.com/path/to/200w/image.png?c=2 200w, https://cdn.example.com/path/to/300w/image.png?c=2 300w, https://cdn.example.com/path/to/1000w/image.png?c=2 1000w', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: '{{getImageSrcset image_with_2_qs 100w="100w" 200w="200w" 300w="300w" 1000w="1000w"}}', | ||
| 38 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2&imbypass=on 100w, https://cdn.example.com/path/to/200w/image.png?c=2&imbypass=on 200w, https://cdn.example.com/path/to/300w/image.png?c=2&imbypass=on 300w, https://cdn.example.com/path/to/1000w/image.png?c=2&imbypass=on 1000w', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{getImageSrcset image 1x="768x768" 2x="1536x1536"}}', | ||
| 42 | output: 'https://cdn.example.com/path/to/768x768/image.png?c=2 1x, https://cdn.example.com/path/to/1536x1536/image.png?c=2 2x', | ||
| 43 | }, | ||
| 44 | ], done); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | it('should return a srcset made of default sizes if requested', function(done) { | |
| 48 | runTestCases([ | ||
| 49 | { | ||
| 50 | input: '{{getImageSrcset image use_default_sizes=true}}', | ||
| 51 | output: 'https://cdn.example.com/path/to/80w/image.png?c=2 80w, https://cdn.example.com/path/to/160w/image.png?c=2 160w, https://cdn.example.com/path/to/320w/image.png?c=2 320w, https://cdn.example.com/path/to/640w/image.png?c=2 640w, https://cdn.example.com/path/to/960w/image.png?c=2 960w, https://cdn.example.com/path/to/1280w/image.png?c=2 1280w, https://cdn.example.com/path/to/1920w/image.png?c=2 1920w, https://cdn.example.com/path/to/2560w/image.png?c=2 2560w', | ||
| 52 | }, | ||
| 53 | ], done); | ||
| 54 | }); | ||
| 55 | |||
| 56 | 1 | it('should return a srcset made of default sizes up to the width of the image if known', function(done) { | |
| 57 | runTestCases([ | ||
| 58 | { | ||
| 59 | input: '{{getImageSrcset image_with_dimensions use_default_sizes=true}}', | ||
| 60 | output: 'https://cdn.example.com/path/to/80w/image.png?c=2 80w, https://cdn.example.com/path/to/160w/image.png?c=2 160w, https://cdn.example.com/path/to/320w/image.png?c=2 320w, https://cdn.example.com/path/to/640w/image.png?c=2 640w, https://cdn.example.com/path/to/960w/image.png?c=2 960w, https://cdn.example.com/path/to/1280w/image.png?c=2 1280w, https://cdn.example.com/path/to/1400w/image.png?c=2 1400w', | ||
| 61 | }, | ||
| 62 | ], done); | ||
| 63 | }); | ||
| 64 | |||
| 65 | 1 | it('should return empty string if no parameters are passed', function(done) { | |
| 66 | runTestCases([ | ||
| 67 | { | ||
| 68 | input: '{{getImageSrcset image}}', | ||
| 69 | output: '', | ||
| 70 | }, | ||
| 71 | ], done); | ||
| 72 | }); | ||
| 73 | |||
| 74 | 1 | it('should return a srcset without a descriptor if a valid image and single srcset size is passed', function(done) { | |
| 75 | runTestCases([ | ||
| 76 | { | ||
| 77 | input: '{{getImageSrcset image 100w="100w"}}', | ||
| 78 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2', | ||
| 79 | }, | ||
| 80 | { | ||
| 81 | input: '{{getImageSrcset image 1x="768x768"}}', | ||
| 82 | output: 'https://cdn.example.com/path/to/768x768/image.png?c=2', | ||
| 83 | }, | ||
| 84 | ], done); | ||
| 85 | }); | ||
| 86 | |||
| 87 | |||
| 88 | 1 | it('should return a url if a url is passed', function(done) { | |
| 89 | runTestCases([ | ||
| 90 | { | ||
| 91 | input: '{{getImageSrcset "http://example.com/image.jpg"}}', | ||
| 92 | output: 'http://example.com/image.jpg', | ||
| 93 | }, | ||
| 94 | { | ||
| 95 | input: '{{getImageSrcset "https://example.com/image.jpg"}}', | ||
| 96 | output: 'https://example.com/image.jpg', | ||
| 97 | }, | ||
| 98 | ], done); | ||
| 99 | }); | ||
| 100 | |||
| 101 | 1 | it('should return empty if srcset array is invalid', function(done) { | |
| 102 | runTestCases([ | ||
| 103 | { | ||
| 104 | input: '{{getImageSrcset image 100="100w"}}', | ||
| 105 | output: '', | ||
| 106 | }, | ||
| 107 | { | ||
| 108 | input: '{{getImageSrcset image abc="def"}}', | ||
| 109 | output: '', | ||
| 110 | }, | ||
| 111 | ], done); | ||
| 112 | }); | ||
| 113 | |||
| 114 | 1 | it('should return empty if image is invalid', function(done) { | |
| 115 | runTestCases([ | ||
| 116 | { | ||
| 117 | input: '{{getImageSrcset not_existing_image 100w="100w" 200w="200w"}}', | ||
| 118 | output: '', | ||
| 119 | }, | ||
| 120 | { | ||
| 121 | input: '{{getImageSrcset not_an_image 100w="100w" 200w="200w"}}', | ||
| 122 | output: '', | ||
| 123 | }, | ||
| 124 | ], done); | ||
| 125 | }); | ||
| 126 | |||
| 127 | 1 | it('should use the default image url if image is invalid', function(done) { | |
| 128 | runTestCases([ | ||
| 129 | { | ||
| 130 | input: '{{getImageSrcset not_an_image "http://image" 100w="100w" 200w="200w"}}', | ||
| 131 | output: 'http://image', | ||
| 132 | }, | ||
| 133 | { | ||
| 134 | input: '{{getImageSrcset not_an_image image_url 100w="100w" 200w="200w"}}', | ||
| 135 | output: context.image_url, | ||
| 136 | }, | ||
| 137 | ], done); | ||
| 138 | }); | ||
| 139 | |||
| 140 | 1 | it('should support lossy compression parameter', function(done) { | |
| 141 | runTestCases([ | ||
| 142 | { | ||
| 143 | input: '{{getImageSrcset image 100w="100w" 200w="200w" lossy=true}}', | ||
| 144 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2&compression=lossy 100w, https://cdn.example.com/path/to/200w/image.png?c=2&compression=lossy 200w', | ||
| 145 | }, | ||
| 146 | { | ||
| 147 | input: '{{getImageSrcset image_with_2_qs 100w="100w" 200w="200w" lossy=true}}', | ||
| 148 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2&imbypass=on&compression=lossy 100w, https://cdn.example.com/path/to/200w/image.png?c=2&imbypass=on&compression=lossy 200w', | ||
| 149 | }, | ||
| 150 | { | ||
| 151 | input: '{{getImageSrcset image 100w="100w" lossy=true}}', | ||
| 152 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2&compression=lossy', | ||
| 153 | }, | ||
| 154 | { | ||
| 155 | input: '{{getImageSrcset image 100w="100w" lossy=false}}', | ||
| 156 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2', | ||
| 157 | }, | ||
| 158 | { | ||
| 159 | input: '{{getImageSrcset image 100w="100w"}}', | ||
| 160 | output: 'https://cdn.example.com/path/to/100w/image.png?c=2', | ||
| 161 | }, | ||
| 162 | ], done); | ||
| 163 | }); | ||
| 164 | }); | ||
| 165 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | 1 | describe('getImageSrcset1x2x helper', function() { | |
| 10 | const urlData = 'https://cdn.example.com/path/to/{:size}/image.png?c=2'; | ||
| 11 | 1 | const context = { | |
| 12 | image_size_small: "123x456", | ||
| 13 | image_size_large: "1000x900", | ||
| 14 | image_url: 'http://example.com/image.png', | ||
| 15 | not_an_image: null, | ||
| 16 | image_without_dimensions: { | ||
| 17 | data: urlData | ||
| 18 | }, | ||
| 19 | image_with_null_dimensions: { | ||
| 20 | data: urlData, | ||
| 21 | width: null, | ||
| 22 | height: null, | ||
| 23 | }, | ||
| 24 | image_with_dimensions: { | ||
| 25 | data: urlData, | ||
| 26 | width: 1400, | ||
| 27 | height: 950, | ||
| 28 | }, | ||
| 29 | image_with_large_dimensions: { | ||
| 30 | data: urlData, | ||
| 31 | width: 5200, | ||
| 32 | height: 5200, | ||
| 33 | }, | ||
| 34 | }; | ||
| 35 | |||
| 36 | 1 | const runTestCases = testRunner({context}); | |
| 37 | |||
| 38 | 1 | it('should return a srcset if a valid image and srcset sizes are passed', function(done) { | |
| 39 | runTestCases([ | ||
| 40 | { | ||
| 41 | input: '{{getImageSrcset1x2x image_with_dimensions "123x456"}}', | ||
| 42 | output: 'https://cdn.example.com/path/to/123x456/image.png?c=2 1x, https://cdn.example.com/path/to/246x912/image.png?c=2 2x', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{getImageSrcset1x2x image_with_dimensions "123x556"}}', | ||
| 46 | output: 'https://cdn.example.com/path/to/123x556/image.png?c=2 1x, https://cdn.example.com/path/to/210x950/image.png?c=2 1.7086x', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{getImageSrcset1x2x image_with_dimensions image_size_small}}', | ||
| 50 | output: 'https://cdn.example.com/path/to/123x456/image.png?c=2 1x, https://cdn.example.com/path/to/246x912/image.png?c=2 2x', | ||
| 51 | }, | ||
| 52 | ], done); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('should return an image url alone (1x) if the 2x size is not within constraints', function(done) { | |
| 56 | runTestCases([ | ||
| 57 | { | ||
| 58 | input: '{{getImageSrcset1x2x image_with_dimensions "1000x900"}}', | ||
| 59 | output: 'https://cdn.example.com/path/to/1000x900/image.png?c=2', | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | input: '{{getImageSrcset1x2x image_with_dimensions image_size_large}}', | ||
| 63 | output: 'https://cdn.example.com/path/to/1000x900/image.png?c=2', | ||
| 64 | }, | ||
| 65 | { | ||
| 66 | input: '{{getImageSrcset1x2x image_with_large_dimensions "2600x2600"}}', | ||
| 67 | output: 'https://cdn.example.com/path/to/2600x2600/image.png?c=2', | ||
| 68 | }, | ||
| 69 | ], done); | ||
| 70 | }); | ||
| 71 | |||
| 72 | 1 | it('should return an image url alone (1x) if image does not have dimesions', function(done) { | |
| 73 | runTestCases([ | ||
| 74 | { | ||
| 75 | input: '{{getImageSrcset1x2x image_with_null_dimensions "1000x900"}}', | ||
| 76 | output: 'https://cdn.example.com/path/to/1000x900/image.png?c=2', | ||
| 77 | }, | ||
| 78 | { | ||
| 79 | input: '{{getImageSrcset1x2x image_without_dimensions "1000x900"}}', | ||
| 80 | output: 'https://cdn.example.com/path/to/1000x900/image.png?c=2', | ||
| 81 | }, | ||
| 82 | ], done); | ||
| 83 | }); | ||
| 84 | |||
| 85 | 1 | it('should throw an exception if an no size argument is passed', function (done) { | |
| 86 | renderString('{{getImageSrcset1x2x image_with_dimensions}}').catch(e => { | ||
| 87 | done(); | ||
| 88 | }); | ||
| 89 | }); | ||
| 90 | |||
| 91 | 1 | it('should throw an exception if an invalid size argument is passed', function (done) { | |
| 92 | renderString('{{getImageSrcset1x2x image_with_dimensions "100px"}}').catch(e => { | ||
| 93 | done(); | ||
| 94 | }); | ||
| 95 | }); | ||
| 96 | |||
| 97 | 1 | it('should support lossy compression parameter', function(done) { | |
| 98 | runTestCases([ | ||
| 99 | { | ||
| 100 | input: '{{getImageSrcset1x2x image_with_dimensions "123x456" lossy=true}}', | ||
| 101 | output: 'https://cdn.example.com/path/to/123x456/image.png?c=2&compression=lossy 1x, https://cdn.example.com/path/to/246x912/image.png?c=2&compression=lossy 2x', | ||
| 102 | }, | ||
| 103 | { | ||
| 104 | input: '{{getImageSrcset1x2x image_with_dimensions "123x456" lossy=false}}', | ||
| 105 | output: 'https://cdn.example.com/path/to/123x456/image.png?c=2 1x, https://cdn.example.com/path/to/246x912/image.png?c=2 2x', | ||
| 106 | }, | ||
| 107 | { | ||
| 108 | input: '{{getImageSrcset1x2x image_with_dimensions "123x456"}}', | ||
| 109 | output: 'https://cdn.example.com/path/to/123x456/image.png?c=2 1x, https://cdn.example.com/path/to/246x912/image.png?c=2 2x', | ||
| 110 | }, | ||
| 111 | ], done); | ||
| 112 | }); | ||
| 113 | }); | ||
| 114 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('getObject helper', function () { | |
| 8 | const context = { | ||
| 9 | obj: { | ||
| 10 | a: { | ||
| 11 | b: { | ||
| 12 | c: 'd' | ||
| 13 | }, | ||
| 14 | array: [1, 2, 3, 4, 5] | ||
| 15 | }, | ||
| 16 | aa: 'a', | ||
| 17 | ab: 'b', | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | 1 | const runTestCases = testRunner({context}); | |
| 22 | |||
| 23 | 1 | it('returns requested prop in correct object type', function (done) { | |
| 24 | runTestCases([ | ||
| 25 | { | ||
| 26 | input: `{{#with (getObject "a.b.c" obj)}}{{c}}{{/with}}`, | ||
| 27 | output: 'd', | ||
| 28 | }, | ||
| 29 | { | ||
| 30 | input: `{{getObject "a.array[2]" obj}}`, | ||
| 31 | output: `3`, | ||
| 32 | } | ||
| 33 | ], done); | ||
| 34 | }); | ||
| 35 | |||
| 36 | 1 | it('does not access prototype props', function (done) { | |
| 37 | context.obj.__proto__ = {x: 'yz'}; | ||
| 38 | 1 | runTestCases([ | |
| 39 | { | ||
| 40 | input: `{{#with (getObject "x" obj)}}{{x}}{{/with}}`, | ||
| 41 | output: ``, | ||
| 42 | }, | ||
| 43 | ], done); | ||
| 44 | }); | ||
| 45 | |||
| 46 | 1 | it('accepts SafeString paths', (done) => { | |
| 47 | runTestCases([ | ||
| 48 | { | ||
| 49 | input: `{{get 'aa' (getObject (concat 'a' 'a') obj)}}`, | ||
| 50 | output: `a`, | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: `{{get 'ab' (getObject (concat 'a' 'b') obj)}}`, | ||
| 54 | output: `b`, | ||
| 55 | } | ||
| 56 | ], done); | ||
| 57 | }); | ||
| 58 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('getShortMonth helper', function() { | |
| 8 | |||
| 9 | const runTestCases = testRunner({}); | ||
| 10 | |||
| 11 | 1 | it('should render an object properly', function(done) { | |
| 12 | runTestCases([ | ||
| 13 | { | ||
| 14 | input: '{{getShortMonth 1}}', | ||
| 15 | output: 'Jan', | ||
| 16 | }, | ||
| 17 | { | ||
| 18 | input: '{{getShortMonth 2}}', | ||
| 19 | output: 'Feb', | ||
| 20 | }, | ||
| 21 | { | ||
| 22 | input: '{{getShortMonth 3}}', | ||
| 23 | output: 'Mar', | ||
| 24 | }, | ||
| 25 | { | ||
| 26 | input: '{{getShortMonth 4}}', | ||
| 27 | output: 'Apr', | ||
| 28 | }, | ||
| 29 | { | ||
| 30 | input: '{{getShortMonth 5}}', | ||
| 31 | output: 'May', | ||
| 32 | }, | ||
| 33 | { | ||
| 34 | input: '{{getShortMonth 6}}', | ||
| 35 | output: 'Jun', | ||
| 36 | }, | ||
| 37 | { | ||
| 38 | input: '{{getShortMonth 7}}', | ||
| 39 | output: 'Jul', | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{getShortMonth 8}}', | ||
| 43 | output: 'Aug', | ||
| 44 | }, | ||
| 45 | { | ||
| 46 | input: '{{getShortMonth 9}}', | ||
| 47 | output: 'Sep', | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | input: '{{getShortMonth 10}}', | ||
| 51 | output: 'Oct', | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | input: '{{getShortMonth 11}}', | ||
| 55 | output: 'Nov', | ||
| 56 | }, | ||
| 57 | { | ||
| 58 | input: '{{getShortMonth 12}}', | ||
| 59 | output: 'Dec', | ||
| 60 | }, | ||
| 61 | ], done); | ||
| 62 | }); | ||
| 63 | }); | ||
| 64 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'), | |
| 2 | Lab = require('lab'), | ||
| 3 | lab = exports.lab = Lab.script(), | ||
| 4 | describe = lab.experiment, | ||
| 5 | expect = Code.expect, | ||
| 6 | it = lab.it, | ||
| 7 | renderString = require('../spec-helpers').renderString; | ||
| 8 | |||
| 9 | 1 | describe('helperMissing', function() { | |
| 10 | it('should return empty string if the helper is missing', function(done) { | ||
| 11 | renderString('{{thisHelperDoesNotExist}}', {}).then(result => { | ||
| 12 | expect(result).to.be.equal(''); | ||
| 13 | 1 | done(); | |
| 14 | }); | ||
| 15 | }); | ||
| 16 | }); | ||
| 17 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | 1 | describe('if helper', () => { | |
| 10 | const context = { | ||
| 11 | num1: 1, | ||
| 12 | num2: 2, | ||
| 13 | product: {a: 1, b: 2}, | ||
| 14 | string: 'yolo', | ||
| 15 | alwaysTrue: true, | ||
| 16 | alwaysFalse: true, | ||
| 17 | big: 'big' | ||
| 18 | }; | ||
| 19 | |||
| 20 | 1 | const runTestCases = testRunner({context}); | |
| 21 | |||
| 22 | 1 | it('should have the same behavior as the original if helper', done => { | |
| 23 | runTestCases([ | ||
| 24 | { | ||
| 25 | input: '{{#if 1}}{{big}}{{/if}}', | ||
| 26 | output: 'big', | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | input: '{{#if 1}}big{{/if}}', | ||
| 30 | output: 'big', | ||
| 31 | }, | ||
| 32 | { | ||
| 33 | input: '{{#if "x"}}big{{/if}}', | ||
| 34 | output: 'big', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: '{{#if ""}}big{{/if}}', | ||
| 38 | output: '', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{#if 0}}big{{/if}}', | ||
| 42 | output: '', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{#if ""}}big{{else}}small{{/if}}', | ||
| 46 | output: 'small', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{#if 0}}big{{else}}small{{/if}}', | ||
| 50 | output: 'small', | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: '{{#if num2}}big{{else}}small{{/if}}', | ||
| 54 | output: 'big', | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | input: '{{#if product}}big{{else}}small{{/if}}', | ||
| 58 | output: 'big', | ||
| 59 | }, | ||
| 60 | { | ||
| 61 | input: '{{#if string}}big{{else}}small{{/if}}', | ||
| 62 | output: 'big', | ||
| 63 | }, | ||
| 64 | ], done); | ||
| 65 | }); | ||
| 66 | |||
| 67 | 1 | it('should render "big" if all conditions match', done => { | |
| 68 | runTestCases([ | ||
| 69 | { | ||
| 70 | input: '{{#if "1" "==" num1}}big{{/if}}', | ||
| 71 | output: 'big', | ||
| 72 | }, | ||
| 73 | { | ||
| 74 | input: '{{#if 1 "===" num1}}big{{/if}}', | ||
| 75 | output: 'big', | ||
| 76 | }, | ||
| 77 | { | ||
| 78 | input: '{{#if 2 "!==" num1}}big{{/if}}', | ||
| 79 | output: 'big', | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | input: '{{#if num2 "!=" num1}}big{{/if}}', | ||
| 83 | output: 'big', | ||
| 84 | }, | ||
| 85 | { | ||
| 86 | input: '{{#if num2 ">" num1}}big{{/if}}', | ||
| 87 | output: 'big', | ||
| 88 | }, | ||
| 89 | { | ||
| 90 | input: '{{#if num1 "<" num2}}big{{/if}}', | ||
| 91 | output: 'big', | ||
| 92 | }, | ||
| 93 | { | ||
| 94 | input: '{{#if num2 ">=" num1}}big{{/if}}', | ||
| 95 | output: 'big', | ||
| 96 | }, | ||
| 97 | { | ||
| 98 | input: '{{#if num1 "<=" num2}}big{{/if}}', | ||
| 99 | output: 'big', | ||
| 100 | }, | ||
| 101 | { | ||
| 102 | input: '{{#if product "typeof" "object"}}big{{/if}}', | ||
| 103 | output: 'big', | ||
| 104 | }, | ||
| 105 | { | ||
| 106 | input: '{{#if "2" "gtnum" "1"}}big{{/if}}', | ||
| 107 | output: 'big', | ||
| 108 | }, | ||
| 109 | { | ||
| 110 | input: '{{#if string "typeof" "string"}}big{{/if}}', | ||
| 111 | output: 'big', | ||
| 112 | }, | ||
| 113 | ], done); | ||
| 114 | }); | ||
| 115 | |||
| 116 | 1 | it('should render empty for all cases', done => { | |
| 117 | runTestCases([ | ||
| 118 | { | ||
| 119 | input: '{{#if "2" "==" num1}}big{{/if}}', | ||
| 120 | output: '', | ||
| 121 | }, | ||
| 122 | { | ||
| 123 | input: '{{#if 2 "===" num1}}big{{/if}}', | ||
| 124 | output: '', | ||
| 125 | }, | ||
| 126 | { | ||
| 127 | input: '{{#if 1 "!==" num1}}big{{/if}}', | ||
| 128 | output: '', | ||
| 129 | }, | ||
| 130 | { | ||
| 131 | input: '{{#if num2 "!=" 2}}big{{/if}}', | ||
| 132 | output: '', | ||
| 133 | }, | ||
| 134 | { | ||
| 135 | input: '{{#if num1 ">" 20}}big{{/if}}', | ||
| 136 | output: '', | ||
| 137 | }, | ||
| 138 | { | ||
| 139 | input: '{{#if 4 "<" num2}}big{{/if}}', | ||
| 140 | output: '', | ||
| 141 | }, | ||
| 142 | { | ||
| 143 | input: '{{#if num1 ">=" 40}}big{{/if}}', | ||
| 144 | output: '', | ||
| 145 | }, | ||
| 146 | { | ||
| 147 | input: '{{#if num2 "<=" num1}}big{{/if}}', | ||
| 148 | output: '', | ||
| 149 | }, | ||
| 150 | { | ||
| 151 | input: '{{#if "1" "gtnum" "2"}}big{{/if}}', | ||
| 152 | output: '', | ||
| 153 | }, | ||
| 154 | { | ||
| 155 | input: '{{#if "1" "gtnum" "1"}}big{{/if}}', | ||
| 156 | output: '', | ||
| 157 | }, | ||
| 158 | { | ||
| 159 | input: '{{#if product "typeof" "string"}}big{{/if}}', | ||
| 160 | output: '', | ||
| 161 | }, | ||
| 162 | { | ||
| 163 | input: '{{#if string "typeof" "object"}}big{{/if}}', | ||
| 164 | output: '', | ||
| 165 | }, | ||
| 166 | ], done); | ||
| 167 | }); | ||
| 168 | |||
| 169 | 1 | it('should ignore additional arguments and process only the first three arguments', done => { | |
| 170 | runTestCases([ | ||
| 171 | { | ||
| 172 | input: '{{#if "1" "<" "2" ">" "3"}}big{{/if}}', | ||
| 173 | output: 'big' | ||
| 174 | }, | ||
| 175 | { | ||
| 176 | input: '{{#if "1" "<" "-1" ">" "3"}}big{{/if}}', | ||
| 177 | output: '' | ||
| 178 | }, | ||
| 179 | { | ||
| 180 | input: '{{#if "1" "<" "1" ">" "3"}}big{{/if}}', | ||
| 181 | output: '' | ||
| 182 | } | ||
| 183 | ], done) | ||
| 184 | }); | ||
| 185 | |||
| 186 | 1 | it('should throw an exception when non string value sent to gtnum', function (done) { | |
| 187 | renderString('{{#if num1 "gtnum" "2"}}big{{/if}}').catch(e => { | ||
| 188 | renderString('{{#if "2" "gtnum" num2}}big{{/if}}').catch(e => { | ||
| 189 | renderString('{{#if num1 "gtnum" num2}}big{{/if}}').catch(e => { | ||
| 190 | done(); | ||
| 191 | }); | ||
| 192 | }); | ||
| 193 | }); | ||
| 194 | }); | ||
| 195 | |||
| 196 | 1 | it('should throw an exception when NaN value sent to gtnum', function (done) { | |
| 197 | renderString('{{#if "aaaa" "gtnum" "2"}}big{{/if}}').catch(e => { | ||
| 198 | renderString('{{#if "2" "gtnum" "bbbb"}}big{{/if}}').catch(e => { | ||
| 199 | renderString('{{#if "aaaa" "gtnum" "bbbb"}}big{{/if}}').catch(e => { | ||
| 200 | done(); | ||
| 201 | }); | ||
| 202 | }); | ||
| 203 | }); | ||
| 204 | }); | ||
| 205 | |||
| 206 | |||
| 207 | 1 | it('should render "big" if all ifs match', done => { | |
| 208 | const context = { | ||
| 209 | num1: 1, | ||
| 210 | num2: 2, | ||
| 211 | product: {a: 1, b: 2}, | ||
| 212 | string: 'yolo' | ||
| 213 | }; | ||
| 214 | |||
| 215 | 1 | runTestCases([ | |
| 216 | { | ||
| 217 | input: '{{#if "1" num1 operator="=="}}big{{/if}}', | ||
| 218 | output: 'big', | ||
| 219 | context: context, | ||
| 220 | }, | ||
| 221 | { | ||
| 222 | input: '{{#if 1 num1 operator="==="}}big{{/if}}', | ||
| 223 | output: 'big', | ||
| 224 | context: context, | ||
| 225 | }, | ||
| 226 | { | ||
| 227 | input: '{{#if 2 num1 operator="!=="}}big{{/if}}', | ||
| 228 | output: 'big', | ||
| 229 | context: context, | ||
| 230 | }, | ||
| 231 | { | ||
| 232 | input: '{{#if num2 num1 operator="!="}}big{{/if}}', | ||
| 233 | output: 'big', | ||
| 234 | context: context, | ||
| 235 | }, | ||
| 236 | { | ||
| 237 | input: '{{#if num2 num1 operator=">"}}big{{/if}}', | ||
| 238 | output: 'big', | ||
| 239 | context: context, | ||
| 240 | }, | ||
| 241 | { | ||
| 242 | input: '{{#if num1 num2 operator="<"}}big{{/if}}', | ||
| 243 | output: 'big', | ||
| 244 | context: context, | ||
| 245 | }, | ||
| 246 | { | ||
| 247 | input: '{{#if num2 num1 operator=">="}}big{{/if}}', | ||
| 248 | output: 'big', | ||
| 249 | context: context, | ||
| 250 | }, | ||
| 251 | { | ||
| 252 | input: '{{#if num1 num2 operator="<="}}big{{/if}}', | ||
| 253 | output: 'big', | ||
| 254 | context: context, | ||
| 255 | }, | ||
| 256 | { | ||
| 257 | input: '{{#if product "object" operator="typeof"}}big{{/if}}', | ||
| 258 | output: 'big', | ||
| 259 | context: context, | ||
| 260 | }, | ||
| 261 | { | ||
| 262 | input: '{{#if string "string" operator="typeof"}}big{{/if}}', | ||
| 263 | output: 'big', | ||
| 264 | context: context, | ||
| 265 | }, | ||
| 266 | ], done); | ||
| 267 | }); | ||
| 268 | |||
| 269 | 1 | it('should render empty for all cases', done => { | |
| 270 | const context = { | ||
| 271 | num1: 1, | ||
| 272 | num2: 2, | ||
| 273 | product: {a: 1, b: 2}, | ||
| 274 | string: 'yolo', | ||
| 275 | emptyArray: [], | ||
| 276 | emptyObject: {} | ||
| 277 | }; | ||
| 278 | |||
| 279 | 1 | runTestCases([ | |
| 280 | { | ||
| 281 | input: '{{#if emptyObject}}big{{/if}}', | ||
| 282 | output: '', | ||
| 283 | context: context, | ||
| 284 | }, | ||
| 285 | { | ||
| 286 | input: '{{#if emptyArray}}big{{/if}}', | ||
| 287 | output: '', | ||
| 288 | context: context, | ||
| 289 | }, | ||
| 290 | { | ||
| 291 | input: '{{#if emptyArray.length}}big{{/if}}', | ||
| 292 | output: '', | ||
| 293 | context: context, | ||
| 294 | }, | ||
| 295 | { | ||
| 296 | input: '{{#if "2" num1 operator="=="}}big{{/if}}', | ||
| 297 | output: '', | ||
| 298 | context: context, | ||
| 299 | }, | ||
| 300 | { | ||
| 301 | input: '{{#if 2 num1 operator="==="}}big{{/if}}', | ||
| 302 | output: '', | ||
| 303 | context: context, | ||
| 304 | }, | ||
| 305 | { | ||
| 306 | input: '{{#if 1 num1 operator="!=="}}big{{/if}}', | ||
| 307 | output: '', | ||
| 308 | context: context, | ||
| 309 | }, | ||
| 310 | { | ||
| 311 | input: '{{#if num2 2 operator="!="}}big{{/if}}', | ||
| 312 | output: '', | ||
| 313 | context: context, | ||
| 314 | }, | ||
| 315 | { | ||
| 316 | input: '{{#if num1 20 operator=">"}}big{{/if}}', | ||
| 317 | output: '', | ||
| 318 | context: context, | ||
| 319 | }, | ||
| 320 | { | ||
| 321 | input: '{{#if 4 num2 operator="<"}}big{{/if}}', | ||
| 322 | output: '', | ||
| 323 | context: context, | ||
| 324 | }, | ||
| 325 | { | ||
| 326 | input: '{{#if num1 40 operator=">="}}big{{/if}}', | ||
| 327 | output: '', | ||
| 328 | context: context, | ||
| 329 | }, | ||
| 330 | { | ||
| 331 | input: '{{#if num2 num1 operator="<="}}big{{/if}}', | ||
| 332 | output: '', | ||
| 333 | context: context, | ||
| 334 | }, | ||
| 335 | { | ||
| 336 | input: '{{#if product "string" operator="typeof"}}big{{/if}}', | ||
| 337 | output: '', | ||
| 338 | context: context, | ||
| 339 | }, | ||
| 340 | { | ||
| 341 | input: '{{#if string "object" operator="typeof"}}big{{/if}}', | ||
| 342 | output: '', | ||
| 343 | context: context, | ||
| 344 | }, | ||
| 345 | ], done); | ||
| 346 | }); | ||
| 347 | |||
| 348 | 1 | it('should work as a non-block helper when used as a subexpression', done => { | |
| 349 | runTestCases([ | ||
| 350 | { | ||
| 351 | input: '{{#if (if num1 "!==" num2)}}{{big}}{{/if}}', | ||
| 352 | output: 'big', | ||
| 353 | }, | ||
| 354 | { | ||
| 355 | input: '{{#all (if num1 "!==" num2) "1" true}}big{{/all}}', | ||
| 356 | output: 'big', | ||
| 357 | }, | ||
| 358 | ], done); | ||
| 359 | }); | ||
| 360 | }); | ||
| 361 | |||
| 362 | 1 | describe('unless helper', () => { | |
| 363 | const context = { | ||
| 364 | num1: 1, | ||
| 365 | num2: 2, | ||
| 366 | product: {a: 1, b: 2}, | ||
| 367 | alwaysTrue: true, | ||
| 368 | alwaysFalse: false, | ||
| 369 | notEmptyArray: [ 1, 2 , 3 ], | ||
| 370 | emptyArray: [], | ||
| 371 | }; | ||
| 372 | |||
| 373 | 1 | const runTestCases = testRunner({context}); | |
| 374 | |||
| 375 | 1 | it('should print hello', done => { | |
| 376 | runTestCases([ | ||
| 377 | { | ||
| 378 | input: '{{#unless num1 "===" num2}}hello{{/unless}}', | ||
| 379 | output: 'hello', | ||
| 380 | }, | ||
| 381 | { | ||
| 382 | input: '{{#unless alwaysFalse}}hello{{/unless}}', | ||
| 383 | output: 'hello', | ||
| 384 | }, | ||
| 385 | { | ||
| 386 | input: '{{#unless does_not_exist}}hello{{/unless}}', | ||
| 387 | output: 'hello', | ||
| 388 | }, | ||
| 389 | ], done); | ||
| 390 | }); | ||
| 391 | |||
| 392 | 1 | it('should print empty', done => { | |
| 393 | runTestCases([ | ||
| 394 | { | ||
| 395 | input: '{{#unless num1 "===" num1}}hello{{/unless}}', | ||
| 396 | output: '', | ||
| 397 | }, | ||
| 398 | { | ||
| 399 | input: '{{#unless alwaysTrue}}hello{{/unless}}', | ||
| 400 | output: '', | ||
| 401 | }, | ||
| 402 | { | ||
| 403 | input: '{{#unless product}}hello{{/unless}}', | ||
| 404 | output: '', | ||
| 405 | }, | ||
| 406 | ], done); | ||
| 407 | }); | ||
| 408 | |||
| 409 | 1 | it('should work with arrays', done => { | |
| 410 | runTestCases([ | ||
| 411 | { | ||
| 412 | input: '{{#unless emptyArray}}foo{{else}}bar{{/unless}}', | ||
| 413 | output: 'foo', | ||
| 414 | }, | ||
| 415 | { | ||
| 416 | input: '{{#unless notEmptyArray}}foo{{else}}bar{{/unless}}', | ||
| 417 | output: 'bar', | ||
| 418 | }, | ||
| 419 | ], done); | ||
| 420 | }); | ||
| 421 | |||
| 422 | 1 | it('should work as a non-block helper when used as a subexpression', done => { | |
| 423 | runTestCases([ | ||
| 424 | { | ||
| 425 | input: '{{#if (unless num1 "===" num2)}}big{{/if}}', | ||
| 426 | output: 'big', | ||
| 427 | }, | ||
| 428 | { | ||
| 429 | input: '{{#all (unless num1 "===" num2) "1" true}}big{{/all}}', | ||
| 430 | output: 'big', | ||
| 431 | }, | ||
| 432 | ], done); | ||
| 433 | }); | ||
| 434 | }) | ||
| 435 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('incrementVar helper', function() { | |
| 11 | const context = { | ||
| 12 | value1: 12 | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | const runTestCases = testRunner({context}); | |
| 16 | |||
| 17 | 1 | it('should throw an exception if the incrementVar key is not a string', function (done) { | |
| 18 | renderString('{{incrementVar 1}}').catch(e => { | ||
| 19 | done(); | ||
| 20 | }); | ||
| 21 | }); | ||
| 22 | |||
| 23 | 1 | it('should correctly increment', function(done) { | |
| 24 | runTestCases([ | ||
| 25 | { | ||
| 26 | input: "{{incrementVar 'data1'}}{{getVar 'data1'}} {{incrementVar 'data1'}}{{getVar 'data1'}} {{incrementVar 'data1'}}{{getVar 'data1'}}", | ||
| 27 | output: '00 11 22', | ||
| 28 | }, | ||
| 29 | ], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should correctly increment an existing variable', function(done) { | |
| 33 | runTestCases([ | ||
| 34 | { | ||
| 35 | input: "{{assignVar 'data1' value1}}{{getVar 'data1'}} {{incrementVar 'data1'}}", | ||
| 36 | output: '12 13', | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | input: "{{assignVar 'data1' 12}}{{getVar 'data1'}} {{incrementVar 'data1'}}", | ||
| 40 | output: '12 13', | ||
| 41 | }, | ||
| 42 | { | ||
| 43 | input: "{{assignVar 'data1' -12}}{{getVar 'data1'}} {{incrementVar 'data1'}}", | ||
| 44 | output: '-12 -11', | ||
| 45 | }, | ||
| 46 | ], done); | ||
| 47 | }); | ||
| 48 | |||
| 49 | 1 | it('should correctly overwrite an existing non-integer variable', function(done) { | |
| 50 | runTestCases([ | ||
| 51 | { | ||
| 52 | input: "{{assignVar 'data1' 'a'}}{{getVar 'data1'}} {{incrementVar 'data1'}}", | ||
| 53 | output: 'a 0', | ||
| 54 | }, | ||
| 55 | ], done); | ||
| 56 | }); | ||
| 57 | |||
| 58 | |||
| 59 | |||
| 60 | 1 | it('should correctly return data accession object proto/constructor', function(done) { | |
| 61 | runTestCases([ | ||
| 62 | { | ||
| 63 | input: "{{incrementVar '__proto__'}}", | ||
| 64 | output: '0', | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | input: "{{incrementVar 'constructor'}}", | ||
| 68 | output: '0', | ||
| 69 | }, | ||
| 70 | ], done); | ||
| 71 | }); | ||
| 72 | }); | ||
| 73 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('inject helper', function() { | |
| 8 | const context = { | ||
| 9 | value1: "Big", | ||
| 10 | value2: "Commerce", | ||
| 11 | }; | ||
| 12 | |||
| 13 | 1 | const runTestCases = testRunner({context}); | |
| 14 | |||
| 15 | 1 | it('should inject variables', function(done) { | |
| 16 | runTestCases([ | ||
| 17 | { | ||
| 18 | input: "{{inject 'data1' value1}}{{inject 'data2' value2}}{{jsContext}}", | ||
| 19 | output: '"{\\"data1\\":\\"Big\\",\\"data2\\":\\"Commerce\\"}"', | ||
| 20 | }, | ||
| 21 | ], done); | ||
| 22 | }); | ||
| 23 | }); | ||
| 24 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('join helper', function() { | |
| 8 | const context = { | ||
| 9 | list: ['Mario', 'Chris', 'Mick', 'Hau', 'Cody'], | ||
| 10 | notArray: null, | ||
| 11 | }; | ||
| 12 | |||
| 13 | 1 | const runTestCases = testRunner({context}); | |
| 14 | |||
| 15 | 1 | it('should print a list of names', function(done) { | |
| 16 | runTestCases([ | ||
| 17 | { | ||
| 18 | input: '{{join list " "}}', | ||
| 19 | output: 'Mario Chris Mick Hau Cody', | ||
| 20 | }, | ||
| 21 | { | ||
| 22 | input: '{{join list ", "}}', | ||
| 23 | output: 'Mario, Chris, Mick, Hau, Cody', | ||
| 24 | }, | ||
| 25 | ], done); | ||
| 26 | }); | ||
| 27 | |||
| 28 | 1 | it('should print a list of names and limit to 3', function(done) { | |
| 29 | runTestCases([ | ||
| 30 | { | ||
| 31 | input: '{{join list " " limit=3}}', | ||
| 32 | output: 'Mario Chris Mick', | ||
| 33 | }, | ||
| 34 | ], done); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should print a list of names and use "and" for the last name', function(done) { | |
| 38 | runTestCases([ | ||
| 39 | { | ||
| 40 | input: '{{join list ", " lastSeparator=" and "}}', | ||
| 41 | output: 'Mario, Chris, Mick, Hau and Cody', | ||
| 42 | }, | ||
| 43 | ], done); | ||
| 44 | }); | ||
| 45 | |||
| 46 | 1 | it('should print a list of names and limit to 3 and use "and" for the last name', function(done) { | |
| 47 | runTestCases([ | ||
| 48 | { | ||
| 49 | input: '{{join list ", " limit=3 lastSeparator=" and "}}', | ||
| 50 | output: 'Mario, Chris and Mick', | ||
| 51 | }, | ||
| 52 | ], done); | ||
| 53 | }); | ||
| 54 | }); | ||
| 55 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('json helper', function() { | |
| 8 | const urlData_2_qs = 'https://cdn.example.com/path/to/{:size}/image.png?c=2&imbypass=on'; | ||
| 9 | 1 | const context = { | |
| 10 | image_with_2_qs: { | ||
| 11 | data: urlData_2_qs | ||
| 12 | }, | ||
| 13 | object: { a: 1, b: "hello" } | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | const runTestCases = testRunner({context}); | |
| 17 | |||
| 18 | 1 | it('should render object to json format', function(done) { | |
| 19 | runTestCases([ | ||
| 20 | { | ||
| 21 | input: '{{{json object}}}', | ||
| 22 | output: '{"a":1,"b":"hello"}', | ||
| 23 | }, | ||
| 24 | ], done); | ||
| 25 | }); | ||
| 26 | 1 | it('should work together with getImage', function(done) { | |
| 27 | runTestCases([ | ||
| 28 | { | ||
| 29 | input: '{{{json (getImage image_with_2_qs)}}}', | ||
| 30 | output: '"https://cdn.example.com/path/to/original/image.png?c=2&imbypass=on"', | ||
| 31 | }, | ||
| 32 | ], done); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should work together with concat', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{{json (concat \'Hello \' \'World\')}}}', | ||
| 39 | output: '"Hello World"', | ||
| 40 | }, | ||
| 41 | ], done); | ||
| 42 | }); | ||
| 43 | }); | ||
| 44 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const { describe, it } = exports.lab = require('lab').script(); | |
| 2 | |||
| 3 | 1 | const { testRunner } = require('../spec-helpers'); | |
| 4 | |||
| 5 | 1 | describe('JSONParseSafe helper', () => { | |
| 6 | const context = { | ||
| 7 | jsonString: '{"name": "John"}', | ||
| 8 | string: 'John Doe', | ||
| 9 | }; | ||
| 10 | 1 | const runTestCases = testRunner({context}); | |
| 11 | |||
| 12 | 1 | it('#Block: should execute the main instruction', done => { | |
| 13 | runTestCases([ | ||
| 14 | { | ||
| 15 | input: '{{#JSONparseSafe jsonString}}{{name}}{{/JSONparseSafe}}', | ||
| 16 | output: 'John', | ||
| 17 | }, | ||
| 18 | ], done); | ||
| 19 | }); | ||
| 20 | |||
| 21 | 1 | it('#Block: should skip the main instruction if variable is non-json', done => { | |
| 22 | runTestCases([ | ||
| 23 | { | ||
| 24 | input: '{{#JSONparseSafe string}}{{name}}{{/JSONparseSafe}}', | ||
| 25 | output: '', | ||
| 26 | }, | ||
| 27 | ], done); | ||
| 28 | }); | ||
| 29 | |||
| 30 | 1 | it('#Block: should execute the else instruction if variable is non-json', done => { | |
| 31 | runTestCases([ | ||
| 32 | { | ||
| 33 | input: '{{#JSONparseSafe string}}{{name}}{{else}}{{string}}{{/JSONparseSafe}}', | ||
| 34 | output: 'John Doe', | ||
| 35 | }, | ||
| 36 | ], done); | ||
| 37 | }); | ||
| 38 | |||
| 39 | // Variation of the Block Helper test to convert to an Inline Call. | ||
| 40 | 1 | it('Inline: Output should match the same as `main test`', done => { | |
| 41 | runTestCases([ | ||
| 42 | { | ||
| 43 | input: '{{#with (JSONparseSafe jsonString)}}{{this.name}}{{/with}}', | ||
| 44 | output: 'John', | ||
| 45 | }, | ||
| 46 | ], done); | ||
| 47 | }); | ||
| 48 | |||
| 49 | 1 | it('Inline: Using JSON.stringify helper, and a empty object; parse the empty object and output it', done => { | |
| 50 | runTestCases([ | ||
| 51 | { | ||
| 52 | input: '{{#with (JSONparseSafe "{}")}}{{json this}}{{/with}}', | ||
| 53 | output: '{}', | ||
| 54 | }, | ||
| 55 | ], done); | ||
| 56 | }); | ||
| 57 | |||
| 58 | |||
| 59 | 1 | it('Inline: Using JSON.stringify helper, and a invalid / malformed object; parse the invalid object and output it, output is a Javascript `undefined` object', done => { | |
| 60 | runTestCases([ | ||
| 61 | { | ||
| 62 | input: '{{#with (JSONparseSafe "{")}}Undefined response due to malformed input{{else}}{{/with}}', | ||
| 63 | output: '', | ||
| 64 | }, | ||
| 65 | ], done); | ||
| 66 | }); | ||
| 67 | |||
| 68 | 1 | it('Inline: Stringifed example - with values', done => { | |
| 69 | runTestCases([ | ||
| 70 | { | ||
| 71 | input: `{{json (JSONparseSafe '{"test": "value"}')}}`, | ||
| 72 | output: "{"test":"value"}", | ||
| 73 | }, | ||
| 74 | ], done); | ||
| 75 | }); | ||
| 76 | |||
| 77 | |||
| 78 | 1 | it('Inline: Stringifed example - without values', done => { | |
| 79 | runTestCases([ | ||
| 80 | { | ||
| 81 | input: `{{json (JSONparseSafe '{}')}}`, | ||
| 82 | output: '{}', | ||
| 83 | }, | ||
| 84 | ], done); | ||
| 85 | }); | ||
| 86 | |||
| 87 | 1 | it('Inline: Stringifed example - malformed input', done => { | |
| 88 | runTestCases([ | ||
| 89 | { | ||
| 90 | input: `{{json (JSONparseSafe '{')}}`, | ||
| 91 | output: '', | ||
| 92 | }, | ||
| 93 | ], done); | ||
| 94 | }); | ||
| 95 | }); | ||
| 96 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Lab = require('lab'); | |
| 4 | 1 | const lab = exports.lab = Lab.script(); | |
| 5 | 1 | const beforeEach = lab.beforeEach; | |
| 6 | 1 | const describe = lab.experiment; | |
| 7 | 1 | const it = lab.it; | |
| 8 | |||
| 9 | 1 | const specHelpers = require('../spec-helpers'); | |
| 10 | 1 | const buildRenderer = specHelpers.buildRenderer; | |
| 11 | 1 | const testRunner = specHelpers.testRunner; | |
| 12 | |||
| 13 | 1 | describe('lang helper', () => { | |
| 14 | let context, renderer, runTestCases; | ||
| 15 | |||
| 16 | 1 | beforeEach(done => { | |
| 17 | context = { | ||
| 18 | name: 'BigCommerce', | ||
| 19 | test: {}, | ||
| 20 | }; | ||
| 21 | |||
| 22 | 3 | renderer = buildRenderer(); | |
| 23 | 3 | renderer.setTranslator({ | |
| 24 | getLocale: () => 'en', | ||
| 25 | translate: (key, params) => `Powered By ${params.name}`, | ||
| 26 | }); | ||
| 27 | |||
| 28 | 3 | runTestCases = testRunner({renderer, context}); | |
| 29 | |||
| 30 | 3 | done(); | |
| 31 | }); | ||
| 32 | |||
| 33 | 1 | it('should translate the key with attributes', done => { | |
| 34 | runTestCases([ | ||
| 35 | { | ||
| 36 | input: '{{lang "powered_by" name=name}}', | ||
| 37 | output: 'Powered By BigCommerce', | ||
| 38 | }, | ||
| 39 | ], done); | ||
| 40 | }); | ||
| 41 | |||
| 42 | 1 | it('should return an empty string if translator is undefined', done => { | |
| 43 | renderer.setTranslator(null); | ||
| 44 | |||
| 45 | 1 | runTestCases([ | |
| 46 | { | ||
| 47 | input: '{{lang "powered_by" name=name}}', | ||
| 48 | output: '', | ||
| 49 | }, | ||
| 50 | ], done); | ||
| 51 | }); | ||
| 52 | |||
| 53 | 1 | it('should return an empty string if translastor returns an object', done => { | |
| 54 | renderer.setTranslator({ | ||
| 55 | getLocale: () => 'en', | ||
| 56 | translate: (key, params) => params.test, | ||
| 57 | }); | ||
| 58 | |||
| 59 | 1 | runTestCases([ | |
| 60 | { | ||
| 61 | input: '{{lang "some_key" name=test}}', | ||
| 62 | output: '', | ||
| 63 | }, | ||
| 64 | ], done); | ||
| 65 | }); | ||
| 66 | }); | ||
| 67 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Lab = require('lab'); | |
| 4 | 1 | const lab = exports.lab = Lab.script(); | |
| 5 | 1 | const beforeEach = lab.beforeEach; | |
| 6 | 1 | const describe = lab.experiment; | |
| 7 | 1 | const it = lab.it; | |
| 8 | |||
| 9 | 1 | const specHelpers = require('../spec-helpers'); | |
| 10 | 1 | const buildRenderer = specHelpers.buildRenderer; | |
| 11 | 1 | const testRunner = specHelpers.testRunner; | |
| 12 | |||
| 13 | 1 | describe('langJson helper', () => { | |
| 14 | let renderer, runTestCases; | ||
| 15 | |||
| 16 | 1 | beforeEach(done => { | |
| 17 | renderer = buildRenderer(); | ||
| 18 | 2 | renderer.setTranslator({ | |
| 19 | getLocale: () => 'en', | ||
| 20 | getLanguage: () => ({ locale: 'en' }), | ||
| 21 | }); | ||
| 22 | |||
| 23 | 2 | runTestCases = testRunner({renderer}); | |
| 24 | |||
| 25 | 2 | done(); | |
| 26 | }); | ||
| 27 | |||
| 28 | 1 | it('should return translation as JSON string if translator is defined', done => { | |
| 29 | runTestCases([ | ||
| 30 | { | ||
| 31 | input: '{{{langJson}}}', | ||
| 32 | output: JSON.stringify(renderer.getTranslator().getLanguage()), | ||
| 33 | }, | ||
| 34 | ], done); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should return an empty object as JSON string if translator is not defined', done => { | |
| 38 | renderer.setTranslator(null); | ||
| 39 | |||
| 40 | 1 | runTestCases([ | |
| 41 | { | ||
| 42 | input: '{{{langJson}}}', | ||
| 43 | output: '{}', | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | }); | ||
| 48 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('limit helper', function() { | |
| 8 | const runTestCases = testRunner({}); | ||
| 9 | |||
| 10 | 1 | it('should limit an array properly', function(done) { | |
| 11 | runTestCases([ | ||
| 12 | { | ||
| 13 | input: '{{#each (limit var 4)}}{{this}} {{/each}}', | ||
| 14 | output: '1 2 3 4 ', | ||
| 15 | context: {var: [1,2,3,4,5,6,7,8]}, | ||
| 16 | }, | ||
| 17 | ], done); | ||
| 18 | }); | ||
| 19 | |||
| 20 | 1 | it('should limit an string properly', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{limit var 10}}', | ||
| 24 | output: 'This is lo', | ||
| 25 | context: {var: "This is longer than the chosen limit"}, | ||
| 26 | }, | ||
| 27 | ], done); | ||
| 28 | }); | ||
| 29 | }); | ||
| 30 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Sinon = require('sinon'); | |
| 2 | 1 | const Lab = require('lab'), | |
| 3 | lab = exports.lab = Lab.script(), | ||
| 4 | describe = lab.experiment, | ||
| 5 | it = lab.it, | ||
| 6 | beforeEach = lab.beforeEach, | ||
| 7 | afterEach = lab.afterEach, | ||
| 8 | testRunner = require('../spec-helpers').testRunner; | ||
| 9 | 1 | const Code = require('code'), | |
| 10 | expect = Code.expect; | ||
| 11 | 1 | const moment = require('moment'); | |
| 12 | |||
| 13 | 1 | describe('moment helper', function () { | |
| 14 | const runTestCases = testRunner({}); | ||
| 15 | |||
| 16 | 1 | it('renders the date in the format specified', function (done) { | |
| 17 | const now = new Date(); | ||
| 18 | 1 | runTestCases([ | |
| 19 | { | ||
| 20 | input: `{{moment "1 year ago" "YYYY"}}`, | ||
| 21 | output: `${now.getFullYear() - 1}`, | ||
| 22 | }, | ||
| 23 | { | ||
| 24 | input: `{{moment "2 days ago" "YYYY"}}`, | ||
| 25 | output: `${now.getFullYear()}`, | ||
| 26 | }, | ||
| 27 | { | ||
| 28 | input: `{{moment '2022-06-29' 'DD/MM/YYYY'}}`, | ||
| 29 | output: '29/06/2022', | ||
| 30 | }, | ||
| 31 | { | ||
| 32 | input: `{{moment '2022-06-30' 'DD/MM/YYYY'}}`, | ||
| 33 | output: '30/06/2022', | ||
| 34 | }, | ||
| 35 | { | ||
| 36 | input: `{{moment '2022-07-29' 'DD/MM/YYYY'}}`, | ||
| 37 | output: '29/07/2022', | ||
| 38 | }, | ||
| 39 | { | ||
| 40 | input: `{{moment '2022-07-30' 'DD/MM/YYYY'}}`, | ||
| 41 | output: '30/07/2022', | ||
| 42 | } | ||
| 43 | ], done); | ||
| 44 | }); | ||
| 45 | |||
| 46 | 1 | it('is backwards compatible with helper-date 0.2.3 null-argument behavior', (done) => { | |
| 47 | runTestCases([ | ||
| 48 | { | ||
| 49 | input: `{{moment format="YYYY"}}`, | ||
| 50 | output: moment().format('YYYY'), | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: `{{moment null null}}`, | ||
| 54 | output: moment().format('MMMM DD, YYYY'), | ||
| 55 | }, | ||
| 56 | { | ||
| 57 | input: `{{moment undefined undefined}}`, | ||
| 58 | output: moment().format('MMMM DD, YYYY'), | ||
| 59 | }, | ||
| 60 | { | ||
| 61 | input: `{{moment "now" format="YYYY"}}`, | ||
| 62 | output: `Invalid date`, | ||
| 63 | } | ||
| 64 | ], done); | ||
| 65 | }); | ||
| 66 | |||
| 67 | 1 | it('permits use of moment.js functions', (done) => { | |
| 68 | runTestCases([ | ||
| 69 | { | ||
| 70 | input: `{{moment "2022-01-01" isAfter="1999-12-31"}}`, | ||
| 71 | output: `true`, | ||
| 72 | } | ||
| 73 | ], done); | ||
| 74 | }); | ||
| 75 | |||
| 76 | 1 | describe('when amount is provided', () => { | |
| 77 | let consoleWarnCopy = console.warn; | ||
| 78 | |||
| 79 | 1 | beforeEach(done => { | |
| 80 | console.warn = Sinon.fake(); | ||
| 81 | 1 | done(); | |
| 82 | }); | ||
| 83 | |||
| 84 | 1 | afterEach(done => { | |
| 85 | console.warn = consoleWarnCopy; | ||
| 86 | 1 | done(); | |
| 87 | }); | ||
| 88 | |||
| 89 | 1 | it('runs console.warn', (done) => { | |
| 90 | runTestCases([ | ||
| 91 | { | ||
| 92 | input: `{{moment "2022-01-01" isAfter="1999-12-31" amount=1}}`, | ||
| 93 | output: `true`, | ||
| 94 | } | ||
| 95 | ], () => { | ||
| 96 | expect(console.warn.called).to.equal(true); | ||
| 97 | 1 | done(); | |
| 98 | }); | ||
| 99 | |||
| 100 | }); | ||
| 101 | }); | ||
| 102 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'); | |
| 2 | 1 | const { expect } = require('code'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const describe = lab.experiment; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const { testRunner, renderString } = require('../spec-helpers'); | |
| 7 | |||
| 8 | 1 | describe('money helper', function() { | |
| 9 | const context = { | ||
| 10 | price: 1234.56, | ||
| 11 | }; | ||
| 12 | 1 | const siteSettings = { | |
| 13 | money: { | ||
| 14 | currency_location: "left", | ||
| 15 | currency_token: "$", | ||
| 16 | decimal_places: 2, | ||
| 17 | thousands_token: ',', | ||
| 18 | decimal_token: '.', | ||
| 19 | } | ||
| 20 | }; | ||
| 21 | |||
| 22 | |||
| 23 | 1 | it('should correctly set money value from money site settings', function(done) { | |
| 24 | const runTestCases = testRunner({context, siteSettings}); | ||
| 25 | 1 | runTestCases([ | |
| 26 | { | ||
| 27 | input: '{{money price}}', | ||
| 28 | output: '$ 1,234.56', | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | input: '{{money 12.34}}', | ||
| 32 | output: '$ 12.34', | ||
| 33 | }, | ||
| 34 | ], done); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should read money site settings from input parameters', function(done) { | |
| 38 | const runTestCases = testRunner({context, siteSettings}); | ||
| 39 | 1 | runTestCases([ | |
| 40 | { | ||
| 41 | input: '{{money price 1}}', | ||
| 42 | output: '$ 1,234.6', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{money price 3}}', | ||
| 46 | output: '$ 1,234.560', | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | input: '{{money price 2 "."}}', | ||
| 50 | output: '$ 1.234.56', | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | input: '{{money price 2 "," ","}}', | ||
| 54 | output: '$ 1,234,56', | ||
| 55 | }, | ||
| 56 | ], done); | ||
| 57 | }); | ||
| 58 | |||
| 59 | 1 | it('should correctly set currency position regardless of location capitalization - [STRF-10878]', function(done) { | |
| 60 | const settingsWithCapitalizedLocation = { | ||
| 61 | money: { | ||
| 62 | currency_location: "Left", | ||
| 63 | currency_token: "$", | ||
| 64 | decimal_places: 2, | ||
| 65 | thousands_token: ',', | ||
| 66 | decimal_token: '.', | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | 1 | const runTestCases = testRunner({ context, siteSettings: settingsWithCapitalizedLocation }); | |
| 71 | 1 | runTestCases([ | |
| 72 | { | ||
| 73 | input: '{{money price}}', | ||
| 74 | output: '$ 1,234.56', | ||
| 75 | }, | ||
| 76 | ], done); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | it('should throw an exception if the price value parameter has an invalid type', function(done) { | |
| 80 | renderString('{{money "hello"}}').catch(err => { | ||
| 81 | expect(err.message.startsWith("money helper accepts only Number's as first parameter")).to.be.true(); | ||
| 82 | 1 | done(); | |
| 83 | }); | ||
| 84 | }); | ||
| 85 | |||
| 86 | 1 | it('should throw an exception if the decimal places parameter has an invalid type', function(done) { | |
| 87 | renderString('{{money 1.2 "hello"}}').catch(err => { | ||
| 88 | expect(err.message.startsWith("money helper accepts only Number's for decimal places")).to.be.true(); | ||
| 89 | 1 | done(); | |
| 90 | }); | ||
| 91 | }); | ||
| 92 | }); | ||
| 93 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | |||
| 8 | 1 | describe('multiConcat helper', function() { | |
| 9 | const context = { | ||
| 10 | string1: "First", | ||
| 11 | string2: "Second", | ||
| 12 | string3: "Third", | ||
| 13 | string4: "Fourth" | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | const runTestCases = testRunner({context}); | |
| 17 | |||
| 18 | 1 | it('should concatenate all strings by default', function(done) { | |
| 19 | runTestCases([ | ||
| 20 | { | ||
| 21 | input: '{{multiConcat string1 string2 string3}}', | ||
| 22 | output: 'FirstSecondThird', | ||
| 23 | }, | ||
| 24 | { | ||
| 25 | input: '{{multiConcat string1 string2 string3 string4}}', | ||
| 26 | output: 'FirstSecondThirdFourth', | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | input: '{{multiConcat string1 string2}}', | ||
| 30 | output: 'FirstSecond', | ||
| 31 | } | ||
| 32 | ], done); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should accept string, number, boolean, empty', function(done) { | |
| 36 | runTestCases([ | ||
| 37 | { | ||
| 38 | input: '{{multiConcat "First" 2}}', | ||
| 39 | output: 'First2', | ||
| 40 | }, | ||
| 41 | { | ||
| 42 | input: '{{multiConcat string1 3 "" "4" true}}', | ||
| 43 | output: 'First34true', | ||
| 44 | }, | ||
| 45 | { | ||
| 46 | input: '{{multiConcat string1 3 false "" "4" true}}', | ||
| 47 | output: 'First3false4true', | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | input: '{{multiConcat string1 ""}}', | ||
| 51 | output: 'First', | ||
| 52 | } | ||
| 53 | ], done); | ||
| 54 | }); | ||
| 55 | }); | ||
| 56 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('nl2br helper', function() { | |
| 8 | const context = { | ||
| 9 | text: "Hello\nmy\nname\nis\nJack" | ||
| 10 | }; | ||
| 11 | |||
| 12 | 1 | const runTestCases = testRunner({context}); | |
| 13 | |||
| 14 | 1 | it('should convert new lines to <br> tags', function(done) { | |
| 15 | runTestCases([ | ||
| 16 | { | ||
| 17 | input: '{{nl2br text}}', | ||
| 18 | output: 'Hello<br>\nmy<br>\nname<br>\nis<br>\nJack', | ||
| 19 | }, | ||
| 20 | ], done); | ||
| 21 | }); | ||
| 22 | }); | ||
| 23 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'); | |
| 2 | 1 | const lab = exports.lab = Lab.script(); | |
| 3 | 1 | const { describe, it} = lab; | |
| 4 | 1 | const {testRunner, buildRenderer, randomString} = require('../spec-helpers'); | |
| 5 | |||
| 6 | 1 | describe('nonce helper', function () { | |
| 7 | const context = {} | ||
| 8 | |||
| 9 | 1 | it('should render a nonce in quotes with the correct value from request params', function (done) { | |
| 10 | const requestParams = { | ||
| 11 | security: { | ||
| 12 | nonce: randomString() | ||
| 13 | }, | ||
| 14 | } | ||
| 15 | 1 | const renderer = buildRenderer({}, {}, {}, null, requestParams); | |
| 16 | 1 | const runTestCases = testRunner({context, renderer}); | |
| 17 | 1 | runTestCases([ | |
| 18 | { | ||
| 19 | input: '{{nonce}}', | ||
| 20 | output: requestParams.security.nonce, | ||
| 21 | }, | ||
| 22 | ], done); | ||
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should not render a nonce since request param is empty', function (done) { | |
| 26 | const requestParams = { | ||
| 27 | security: {}, | ||
| 28 | } | ||
| 29 | 1 | const renderer = buildRenderer({}, {}, {}, null, requestParams); | |
| 30 | 1 | const runTestCases = testRunner({context, renderer}); | |
| 31 | 1 | runTestCases([ | |
| 32 | { | ||
| 33 | input: '{{nonce}}', | ||
| 34 | output: '', | ||
| 35 | }, | ||
| 36 | ], done); | ||
| 37 | }); | ||
| 38 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('occurrences helper', function() { | |
| 8 | const context = { | ||
| 9 | string: "asdf asdf2 xxx yyy", | ||
| 10 | number: 1, | ||
| 11 | object: { | ||
| 12 | asdf: 1, | ||
| 13 | asdf2: 2, | ||
| 14 | xxx: 3, | ||
| 15 | yyy: 4, | ||
| 16 | }, | ||
| 17 | array: ['asdf', 'asdf2', 'xxx', 'yyy'] | ||
| 18 | }; | ||
| 19 | |||
| 20 | 1 | const runTestCases = testRunner({context}); | |
| 21 | |||
| 22 | 1 | it('should count number of occurrences of substring', function(done) { | |
| 23 | runTestCases([ | ||
| 24 | { | ||
| 25 | input: "{{occurrences string 'asdf'}}", | ||
| 26 | output: '2', | ||
| 27 | }, | ||
| 28 | { | ||
| 29 | input: "{{occurrences string 'xxx'}}", | ||
| 30 | output: '1', | ||
| 31 | }, | ||
| 32 | { | ||
| 33 | input: "{{occurrences string 'x'}}", | ||
| 34 | output: '3', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: "{{occurrences string 'zzz'}}", | ||
| 38 | output: '0', | ||
| 39 | }, | ||
| 40 | ], done); | ||
| 41 | }); | ||
| 42 | |||
| 43 | 1 | it('should ignore non-strings', function(done) { | |
| 44 | runTestCases([ | ||
| 45 | { | ||
| 46 | input: "{{occurrences object 'asdf'}}", | ||
| 47 | output: '0', | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | input: "{{occurrences array 'asdf'}}", | ||
| 51 | output: '0', | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | input: "{{occurrences number '1'}}", | ||
| 55 | output: '0', | ||
| 56 | }, | ||
| 57 | { | ||
| 58 | input: "{{occurrences string object}}", | ||
| 59 | output: '0', | ||
| 60 | }, | ||
| 61 | { | ||
| 62 | input: "{{occurrences string array}}", | ||
| 63 | output: '0', | ||
| 64 | }, | ||
| 65 | { | ||
| 66 | input: "{{occurrences '1' number}}", | ||
| 67 | output: '0', | ||
| 68 | }, | ||
| 69 | ], done); | ||
| 70 | }); | ||
| 71 | |||
| 72 | 1 | it('should ignore empty strings', function(done) { | |
| 73 | runTestCases([ | ||
| 74 | { | ||
| 75 | input: "{{occurrences '' 'asdf'}}", | ||
| 76 | output: '0', | ||
| 77 | }, | ||
| 78 | { | ||
| 79 | input: "{{occurrences string ''}}", | ||
| 80 | output: '0', | ||
| 81 | }, | ||
| 82 | ], done); | ||
| 83 | }); | ||
| 84 | }); | ||
| 85 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('option helper', function () { | |
| 8 | const context = { | ||
| 9 | array: [1, 2, 3, 4, 5], | ||
| 10 | options: { a: { b: { c: 'd' } } } | ||
| 11 | }; | ||
| 12 | |||
| 13 | 1 | const runTestCases = testRunner({context}); | |
| 14 | |||
| 15 | 1 | it('returns the nested prop of this.options', function (done) { | |
| 16 | runTestCases([ | ||
| 17 | { | ||
| 18 | input: `{{option "a.b.c"}}`, | ||
| 19 | output: 'd', | ||
| 20 | }, | ||
| 21 | ], done); | ||
| 22 | }); | ||
| 23 | |||
| 24 | 1 | it('does not access prototype props', function (done) { | |
| 25 | context.options.__proto__ = {x: 'yz'}; | ||
| 26 | 1 | runTestCases([ | |
| 27 | { | ||
| 28 | input: `{{option "x"}}`, | ||
| 29 | output: ``, | ||
| 30 | }, | ||
| 31 | ], done); | ||
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('accepts SafeString paths', (done) => { | |
| 35 | runTestCases([ | ||
| 36 | { | ||
| 37 | input: `{{option (concat 'a.b.' 'c')}}`, | ||
| 38 | output: `d`, | ||
| 39 | }, | ||
| 40 | ], done); | ||
| 41 | }); | ||
| 42 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('or helper', function() { | |
| 8 | const context = { | ||
| 9 | num1: 1, | ||
| 10 | num2: 2, | ||
| 11 | product: {a: 1, b: 2}, | ||
| 12 | string: 'yolo', | ||
| 13 | alwaysTrue: true, | ||
| 14 | alwaysFalse: false, | ||
| 15 | emptyArray: [], | ||
| 16 | emptyObject: {}, | ||
| 17 | itemArray: [1,2], | ||
| 18 | big: 'big', | ||
| 19 | arrayWithObjs: [{a: 1},{b: 1},{a: 2}] | ||
| 20 | }; | ||
| 21 | |||
| 22 | 1 | const runTestCases = testRunner({context}); | |
| 23 | |||
| 24 | 1 | it('should return "big" if at least one arg valid', function(done) { | |
| 25 | runTestCases([ | ||
| 26 | { | ||
| 27 | input: '{{#or arrayWithObjs string}}{{big}}{{/or}}', | ||
| 28 | output: 'big', | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | input: '{{#or "something test" itemArray}}{{big}}{{/or}}', | ||
| 32 | output: 'big', | ||
| 33 | }, | ||
| 34 | { | ||
| 35 | input: '{{#or "this is before the other test"}}{{big}}{{/or}}', | ||
| 36 | output: 'big', | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | input: '{{#or alwaysFalse emptyArray string}}{{big}}{{/or}}', | ||
| 40 | output: 'big', | ||
| 41 | }, | ||
| 42 | ], done); | ||
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('should return "" when no arguments are valid', function(done) { | |
| 46 | runTestCases([ | ||
| 47 | { | ||
| 48 | input: '{{#or emptyArray emptyObject alwaysFalse}}{{big}}{{/or}}', | ||
| 49 | output: '', | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | input: '{{#or "" false}}{{big}}{{/or}}', | ||
| 53 | output: '', | ||
| 54 | }, | ||
| 55 | ], done); | ||
| 56 | }); | ||
| 57 | }); | ||
| 58 | |||
| 59 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('pluck helper', function() { | |
| 8 | const context = { | ||
| 9 | users: [ | ||
| 10 | { 'user': 'barney', 'age': 36 }, | ||
| 11 | { 'user': 'fred', 'age': 40 } | ||
| 12 | ], | ||
| 13 | null: null, | ||
| 14 | undefined: undefined, | ||
| 15 | }; | ||
| 16 | |||
| 17 | 1 | const runTestCases = testRunner({context}); | |
| 18 | |||
| 19 | 1 | it('should get the values from all elements in collection', function(done) { | |
| 20 | runTestCases([ | ||
| 21 | { | ||
| 22 | input: '{{pluck users "age"}}', | ||
| 23 | output: '36,40', | ||
| 24 | }, | ||
| 25 | { | ||
| 26 | input: '{{#each (pluck users "user")}}hello {{this}} {{/each}}', | ||
| 27 | output: 'hello barney hello fred ', | ||
| 28 | }, | ||
| 29 | ], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should return undefined when accessing proto/constructor', function(done) { | |
| 33 | runTestCases([ | ||
| 34 | { | ||
| 35 | input: '{{pluck users "__proto__"}}', | ||
| 36 | output: ',', | ||
| 37 | }, | ||
| 38 | { | ||
| 39 | input: '{{pluck users "constructor"}}', | ||
| 40 | output: ',', | ||
| 41 | }, | ||
| 42 | ], done); | ||
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('should return empty array when null is provided', function(done) { | |
| 46 | runTestCases([ | ||
| 47 | { | ||
| 48 | input: '{{pluck null "age"}}', | ||
| 49 | output: '', | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | input: '{{pluck undefined "age"}}', | ||
| 53 | output: '', | ||
| 54 | }, | ||
| 55 | ], done); | ||
| 56 | }); | ||
| 57 | }); | ||
| 58 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('pre helper', function() { | |
| 8 | const runTestCases = testRunner({}); | ||
| 9 | |||
| 10 | 1 | it('should render an object properly', function(done) { | |
| 11 | runTestCases([ | ||
| 12 | { | ||
| 13 | input: '{{{pre var}}}', | ||
| 14 | output: '<pre>{}</pre>', | ||
| 15 | context: {var: {}}, | ||
| 16 | }, | ||
| 17 | ], done); | ||
| 18 | }); | ||
| 19 | |||
| 20 | 1 | it('should scape html entities', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{{pre var}}}', | ||
| 24 | output: '<pre>"<div>&\\"500\\"</div>"</pre>', | ||
| 25 | context: {var: "<div>&\"500\"</div>"}, | ||
| 26 | }, | ||
| 27 | ], done); | ||
| 28 | }); | ||
| 29 | }); | ||
| 30 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Lab = require('lab'); | |
| 4 | 1 | const lab = exports.lab = Lab.script(); | |
| 5 | 1 | const before = lab.before; | |
| 6 | 1 | const describe = lab.experiment; | |
| 7 | 1 | const it = lab.it; | |
| 8 | |||
| 9 | 1 | const specHelpers = require('../spec-helpers'); | |
| 10 | 1 | const buildRenderer = specHelpers.buildRenderer; | |
| 11 | 1 | const testRunner = specHelpers.testRunner; | |
| 12 | |||
| 13 | 1 | describe('Region Helper', () => { | |
| 14 | let context, renderer, runTestCases; | ||
| 15 | |||
| 16 | 1 | before(done => { | |
| 17 | context = { | ||
| 18 | 'banner-top': "hello world" | ||
| 19 | }; | ||
| 20 | |||
| 21 | 1 | renderer = buildRenderer(); | |
| 22 | 1 | renderer.setContent(context); | |
| 23 | |||
| 24 | 1 | runTestCases = testRunner({context, renderer}); | |
| 25 | |||
| 26 | 1 | done(); | |
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should return an empty container if using empty content context', done => { | |
| 30 | runTestCases([ | ||
| 31 | { | ||
| 32 | input: '{{region name="banner-bottom" translation="i18n.RegionName.TestingTranslation"}}', | ||
| 33 | output: '<div data-content-region="banner-bottom" data-content-region-translation="i18n.RegionName.TestingTranslation"></div>', | ||
| 34 | renderer: buildRenderer(), | ||
| 35 | }, | ||
| 36 | ], done); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | it('should return an empty container if no matching region on context object', done => { | |
| 40 | runTestCases([ | ||
| 41 | { | ||
| 42 | input: '{{region name="banner-bottom" translation="i18n.RegionName.TestingTranslation"}}', | ||
| 43 | output: '<div data-content-region="banner-bottom" data-content-region-translation="i18n.RegionName.TestingTranslation"></div>', | ||
| 44 | }, | ||
| 45 | ], done); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should return without region translation data attribute if no translation is provided', done => { | |
| 49 | runTestCases([ | ||
| 50 | { | ||
| 51 | input: '{{region name="banner-bottom"}}', | ||
| 52 | output: '<div data-content-region="banner-bottom"></div>', | ||
| 53 | }, | ||
| 54 | ], done); | ||
| 55 | }); | ||
| 56 | |||
| 57 | 1 | it('should return Hello World', done => { | |
| 58 | runTestCases([ | ||
| 59 | { | ||
| 60 | input: '{{region name="banner-top" translation="i18n.RegionName.TestingTranslation"}}', | ||
| 61 | output: '<div data-content-region="banner-top" data-content-region-translation="i18n.RegionName.TestingTranslation">hello world</div>', | ||
| 62 | }, | ||
| 63 | ], done); | ||
| 64 | }); | ||
| 65 | }); | ||
| 66 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('replace helper', function() { | |
| 8 | const templates = { | ||
| 9 | template2: 'day', | ||
| 10 | }; | ||
| 11 | |||
| 12 | 1 | const context = { | |
| 13 | content: "Either you run the %%var%% or the %%var%% runs you", | ||
| 14 | price: '$49.99', | ||
| 15 | facet: 'brand', | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context, templates}); | |
| 19 | |||
| 20 | 1 | it('should replace all ocurrance of %%var%% with "day"', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: "{{#replace '%%var%%' content}}{{> template2}}{{/replace}}", | ||
| 24 | output: 'Either you run the day or the day runs you', | ||
| 25 | }, | ||
| 26 | ], done); | ||
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should handle undefined values', function(done) { | |
| 30 | runTestCases([ | ||
| 31 | { | ||
| 32 | input: "{{#replace '%%var%%' content}}{{> template2}}{{/replace}}", | ||
| 33 | output: '', | ||
| 34 | context: {}, | ||
| 35 | }, | ||
| 36 | ], done); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | it('should replace $', function(done) { | |
| 40 | runTestCases([ | ||
| 41 | { | ||
| 42 | input: "{{#replace '$' price}}{{/replace}}", | ||
| 43 | output: '49.99', | ||
| 44 | }, | ||
| 45 | { | ||
| 46 | input: "{{#replace '$' '$10.00'}}{{/replace}}", | ||
| 47 | output: '10.00', | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | input: "{{#replace '$' '$10.00'}}USD {{/replace}}", | ||
| 51 | output: 'USD 10.00', | ||
| 52 | }, | ||
| 53 | ], done); | ||
| 54 | }); | ||
| 55 | |||
| 56 | 1 | it('should work nicely with other helpers that use safestring', function(done) { | |
| 57 | runTestCases([ | ||
| 58 | { | ||
| 59 | input: "Replace+Concat+Hyphenated: fifth-{{#replace '&' (concat '&' (hyphenate facet)) }}{{/replace}}", | ||
| 60 | output: "Replace+Concat+Hyphenated: fifth-brand", | ||
| 61 | }, | ||
| 62 | ], done); | ||
| 63 | }); | ||
| 64 | |||
| 65 | 1 | it('should gracefully handle not strings', function(done) { | |
| 66 | runTestCases([ | ||
| 67 | { | ||
| 68 | input: "{{#replace something price}}{{/replace}}", | ||
| 69 | output: '', | ||
| 70 | }, | ||
| 71 | { | ||
| 72 | input: "{{#replace $ '$10.00'}}{{/replace}}", | ||
| 73 | output: '', | ||
| 74 | }, | ||
| 75 | { | ||
| 76 | input: "{{#replace foo bar}}{{/replace}}", | ||
| 77 | output: '', | ||
| 78 | }, | ||
| 79 | ], done); | ||
| 80 | }); | ||
| 81 | }); | ||
| 82 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | {testRunner, buildRenderer} = require('../spec-helpers'); | ||
| 6 | 1 | const {expect} = require("code"); | |
| 7 | 1 | const { | |
| 8 | resourceHintAllowedCors, | ||
| 9 | resourceHintAllowedStates, | ||
| 10 | resourceHintAllowedTypes | ||
| 11 | } = require('../../helpers/lib/resourceHints'); | ||
| 12 | |||
| 13 | 1 | describe('resourceHints', function () { | |
| 14 | it('should return the expected resource links', function (done) { | ||
| 15 | const themeSettings = { | ||
| 16 | 'test1-font': 'Google_Open+Sans', | ||
| 17 | 'test2-font': 'Google_Open+Sans_400italic', | ||
| 18 | 'test3-font': 'Google_Open+Sans_700', | ||
| 19 | 'test4-font': 'Google_Karla_700', | ||
| 20 | 'test5-font': 'Google_Lora_400_sans', | ||
| 21 | 'test6-font': 'Google_Volkron', | ||
| 22 | 'test7-font': 'Google_Droid_400,700', | ||
| 23 | 'test8-font': 'Google_Crimson+Text_400,700_sans', | ||
| 24 | 'random-property': 'not a font' | ||
| 25 | }; | ||
| 26 | |||
| 27 | 1 | const renderer = buildRenderer({}, themeSettings); | |
| 28 | 1 | const runTestCases = testRunner({renderer}); | |
| 29 | |||
| 30 | 1 | runTestCases([ | |
| 31 | { | ||
| 32 | input: '{{resourceHints}}', | ||
| 33 | output: '<link rel="dns-prefetch preconnect" href="https://fonts.googleapis.com/" crossorigin><link rel="dns-prefetch preconnect" href="https://fonts.gstatic.com/" crossorigin>', | ||
| 34 | }, | ||
| 35 | ], () => { | ||
| 36 | const hints = renderer.getResourceHints(); | ||
| 37 | 1 | expect(hints).to.have.length(2); | |
| 38 | 1 | hints.forEach(hint => { | |
| 39 | expect(hint.cors).to.equals(resourceHintAllowedCors.anonymousCors); | ||
| 40 | 2 | expect(hint.type).to.equals(resourceHintAllowedTypes.resourceHintFontType); | |
| 41 | 2 | expect(hint.state).to.equals(resourceHintAllowedStates.preconnectResourceHintState); | |
| 42 | }); | ||
| 43 | 1 | done(); | |
| 44 | }); | ||
| 45 | }); | ||
| 46 | }); | ||
| 47 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = specHelpers.testRunner, | ||
| 7 | renderString = specHelpers.renderString; | ||
| 8 | |||
| 9 | |||
| 10 | 1 | describe('setURLQueryParam helper', function() { | |
| 11 | const context = { | ||
| 12 | image_url: 'http://example.com/image.png', | ||
| 13 | not_a_url: null, | ||
| 14 | key: 'referrer', | ||
| 15 | value: 'google', | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context}); | |
| 19 | |||
| 20 | 1 | it('should return a URL with an added parameter given correct input', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{setURLQueryParam "http://example.com/image.jpg" key value}}', | ||
| 24 | output: `http://example.com/image.jpg?${context.key}=${context.value}`, | ||
| 25 | }, | ||
| 26 | { | ||
| 27 | input: '{{setURLQueryParam image_url "key" "value"}}', | ||
| 28 | output: `${context.image_url}?key=value`, | ||
| 29 | }, | ||
| 30 | { | ||
| 31 | input: '{{setURLQueryParam (setURLQueryParam image_url "key" "value") "key2" "value2"}}', | ||
| 32 | output: `${context.image_url}?key=value&key2=value2`, | ||
| 33 | }, | ||
| 34 | { | ||
| 35 | input: '{{setURLQueryParam "http://example.com/product-1?sku=abc123" "key" "value"}}', | ||
| 36 | output: 'http://example.com/product-1?sku=abc123&key=value', | ||
| 37 | }, | ||
| 38 | ], done); | ||
| 39 | }); | ||
| 40 | |||
| 41 | 1 | it('should return a URL with an updated parameter given correct input', function(done) { | |
| 42 | runTestCases([ | ||
| 43 | { | ||
| 44 | input: '{{setURLQueryParam "http://example.com/image.jpg?c=2" "c" "3"}}', | ||
| 45 | output: `http://example.com/image.jpg?c=3`, | ||
| 46 | }, | ||
| 47 | { | ||
| 48 | input: '{{setURLQueryParam "http://example.com/image.jpg?a=1&c=2" "c" "3"}}', | ||
| 49 | output: `http://example.com/image.jpg?a=1&c=3`, | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | input: '{{setURLQueryParam (setURLQueryParam "http://example.com/image.jpg?a=1&c=2" "a" "2") "c" "3"}}', | ||
| 53 | output: `http://example.com/image.jpg?a=2&c=3`, | ||
| 54 | }, | ||
| 55 | { | ||
| 56 | input: '{{setURLQueryParam "http://example.com/product-1?sku=abc123" "sku" "def456"}}', | ||
| 57 | output: 'http://example.com/product-1?sku=def456', | ||
| 58 | }, | ||
| 59 | ], done); | ||
| 60 | }); | ||
| 61 | |||
| 62 | 1 | it('should throw an exception if a url is passed with no parameters', function (done) { | |
| 63 | renderString('{{setURLQueryParam "http://example.com/image.jpg"}}').catch(e => { | ||
| 64 | renderString('{{setURLQueryParam urlData}}').catch(e => { | ||
| 65 | done(); | ||
| 66 | }); | ||
| 67 | }); | ||
| 68 | }); | ||
| 69 | |||
| 70 | 1 | it('should throw an exception if a url is passed with only one parameter', function (done) { | |
| 71 | renderString('{{setURLQueryParam "http://example.com/image.jpg" "key"}}').catch(e => { | ||
| 72 | renderString('{{setURLQueryParam urlData key}}').catch(e => { | ||
| 73 | done(); | ||
| 74 | }); | ||
| 75 | }); | ||
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('should throw an exception if an invalid URL is passed', function (done) { | |
| 79 | renderString('{{setURLQueryParam not_a_url key value}}').catch(e => { | ||
| 80 | renderString('{{setURLQueryParam not_a_url "key" "value"}}').catch(e => { | ||
| 81 | renderString('{{setURLQueryParam "not_a_url" "key" "value"}}').catch(e => { | ||
| 82 | renderString('{{setURLQueryParam "not_a_url" key value}}').catch(e => { | ||
| 83 | done(); | ||
| 84 | }); | ||
| 85 | }); | ||
| 86 | }); | ||
| 87 | }); | ||
| 88 | }); | ||
| 89 | }); | ||
| 90 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('snippet helper', function() { | |
| 8 | |||
| 9 | const runTestCases = testRunner({}); | ||
| 10 | |||
| 11 | 1 | it('should render a comment', function(done) { | |
| 12 | runTestCases([ | ||
| 13 | { | ||
| 14 | input: '{{{snippet "header"}}}', | ||
| 15 | output: '<!-- snippet location header -->', | ||
| 16 | }, | ||
| 17 | ], done); | ||
| 18 | }); | ||
| 19 | }); | ||
| 20 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | specHelpers = require('../spec-helpers'), | ||
| 6 | testRunner = require('../spec-helpers').testRunner, | ||
| 7 | renderString = specHelpers.renderString;; | ||
| 8 | |||
| 9 | 1 | describe('strReplace helper', function() { | |
| 10 | const context = { | ||
| 11 | string: "My name is Albe Albe Albe", | ||
| 12 | substr: "Albe", | ||
| 13 | newSubstr: "Alex", | ||
| 14 | object: {} | ||
| 15 | }; | ||
| 16 | |||
| 17 | 1 | const runTestCases = testRunner({context}); | |
| 18 | |||
| 19 | 1 | it('should replace all by default', function(done) { | |
| 20 | runTestCases([ | ||
| 21 | { | ||
| 22 | input: '{{strReplace string substr newSubstr}}', | ||
| 23 | output: 'My name is Alex Alex Alex', | ||
| 24 | }, | ||
| 25 | { | ||
| 26 | input: '{{strReplace "Your name is none" "none" "Bob"}}', | ||
| 27 | output: 'Your name is Bob', | ||
| 28 | }, | ||
| 29 | ], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should remove characters from string', function(done) { | |
| 33 | runTestCases([ | ||
| 34 | { | ||
| 35 | input: '{{strReplace "123-45-6789" "-" ""}}', | ||
| 36 | output: '123456789', | ||
| 37 | }, | ||
| 38 | ], done); | ||
| 39 | }); | ||
| 40 | |||
| 41 | 1 | it('should replace multiple if given quantity', function(done) { | |
| 42 | runTestCases([ | ||
| 43 | { | ||
| 44 | input: '{{strReplace string substr newSubstr -5}}', | ||
| 45 | output: 'My name is Albe Albe Albe', | ||
| 46 | }, | ||
| 47 | { | ||
| 48 | input: '{{strReplace string substr newSubstr 0}}', | ||
| 49 | output: 'My name is Albe Albe Albe', | ||
| 50 | }, | ||
| 51 | { | ||
| 52 | input: '{{strReplace string substr newSubstr 2}}', | ||
| 53 | output: 'My name is Alex Alex Albe', | ||
| 54 | }, | ||
| 55 | { | ||
| 56 | input: '{{strReplace string substr newSubstr 4}}', | ||
| 57 | output: 'My name is Alex Alex Alex', | ||
| 58 | }, | ||
| 59 | { | ||
| 60 | input: '{{strReplace string substr newSubstr 100}}', | ||
| 61 | output: 'My name is Alex Alex Alex', | ||
| 62 | }, | ||
| 63 | ], done); | ||
| 64 | }); | ||
| 65 | |||
| 66 | 1 | it('should throw an exception if the parameters have an invalid type', function(done) { | |
| 67 | renderString('{{strReplace object "none" "Bob"}}').catch(e => { | ||
| 68 | renderString('{{strReplace "none" 3 "Bob"}}').catch(e => { | ||
| 69 | renderString('{{strReplace "none" "Bob" object}}').catch(e => { | ||
| 70 | renderString('{{strReplace string substr newSubstr "3"}}').catch(e => { | ||
| 71 | done(); | ||
| 72 | }); | ||
| 73 | }); | ||
| 74 | }); | ||
| 75 | }); | ||
| 76 | }); | ||
| 77 | }); | ||
| 78 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | const themeSettings = { | |
| 8 | logo_image: '600x300', | ||
| 9 | }; | ||
| 10 | |||
| 11 | 1 | describe('stripQuerystring helper', function() { | |
| 12 | const urlData_2_qs = 'https://cdn.example.com/path/to/{:size}/image.png?c=2&imbypass=on'; | ||
| 13 | 1 | const context = { | |
| 14 | image_with_2_qs: { | ||
| 15 | data: urlData_2_qs | ||
| 16 | }, | ||
| 17 | }; | ||
| 18 | |||
| 19 | 1 | const runTestCases = testRunner({context, themeSettings}); | |
| 20 | |||
| 21 | 1 | it('strips the query string from a given url', function(done) { | |
| 22 | runTestCases([{ | ||
| 23 | input: `{{stripQuerystring 'http://www.example.com?foo=1&bar=2&baz=3'}}`, | ||
| 24 | output: 'http://www.example.com', | ||
| 25 | }, | ||
| 26 | ], done); | ||
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should work with the getImageSrcset helper as a nested expression', function(done) { | |
| 30 | runTestCases([{ | ||
| 31 | input: `{{stripQuerystring (getImageSrcset image_with_2_qs 1x='100x100')}}`, | ||
| 32 | output: 'https://cdn.example.com/path/to/100x100/image.png', | ||
| 33 | }, | ||
| 34 | ], done); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should work with the getImage helper as a nested expression', function(done) { | |
| 38 | runTestCases([ | ||
| 39 | { | ||
| 40 | input: '{{stripQuerystring (getImage image_with_2_qs "logo_image")}}', | ||
| 41 | output: 'https://cdn.example.com/path/to/600x300/image.png', | ||
| 42 | }, | ||
| 43 | ], done); | ||
| 44 | }); | ||
| 45 | }); | ||
| 46 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it; | ||
| 5 | 1 | const {buildRenderer, testRunner} = require("../spec-helpers"); | |
| 6 | 1 | const {expect} = require("code"); | |
| 7 | 1 | const {resourceHintStyleType} = require("../../helpers/lib/resourceHints").resourceHintAllowedTypes; | |
| 8 | 1 | const {anonymousCors, noCors} = require("../../helpers/lib/resourceHints").resourceHintAllowedCors; | |
| 9 | |||
| 10 | 1 | describe('stylesheet helper', () => { | |
| 11 | const siteSettings = { | ||
| 12 | cdn_url: 'https://cdn.bcapp/hash', | ||
| 13 | theme_version_id: '123', | ||
| 14 | theme_config_id: 'xyz', | ||
| 15 | }; | ||
| 16 | |||
| 17 | 1 | const runTestCases = testRunner({siteSettings}); | |
| 18 | |||
| 19 | 1 | it('should render a link tag with the cdn ulr and stencil-stylesheet data tag', done => { | |
| 20 | const renderer = buildRenderer(siteSettings); | ||
| 21 | 1 | const runner = testRunner({renderer}); | |
| 22 | 1 | runner([ | |
| 23 | { | ||
| 24 | input: '{{{stylesheet "assets/css/style.css" crossorigin="anonymous"}}}', | ||
| 25 | output: '<link data-stencil-stylesheet href="https://cdn.bcapp/hash/stencil/123/css/style-xyz.css" rel="stylesheet" crossorigin="anonymous">', | ||
| 26 | }, | ||
| 27 | ], () => { | ||
| 28 | const hints = renderer.getResourceHints(); | ||
| 29 | 1 | expect(hints.length).to.equals(1); | |
| 30 | 1 | hints.forEach(({src, type, state, cors}) => { | |
| 31 | expect(src).to.startWith(siteSettings.cdn_url); | ||
| 32 | 1 | expect(type).to.equals(resourceHintStyleType); | |
| 33 | 1 | expect(state).to.equals('preload'); | |
| 34 | 1 | expect(cors).to.equals(anonymousCors); | |
| 35 | }); | ||
| 36 | 1 | done(); | |
| 37 | }); | ||
| 38 | }); | ||
| 39 | |||
| 40 | 1 | it('should render both link tags correclty but produce only one resource hint', done => { | |
| 41 | const renderer = buildRenderer(siteSettings); | ||
| 42 | 1 | const context = { | |
| 43 | globals: { | ||
| 44 | resourceHints: [ | ||
| 45 | {src: 'https://cdn.bcapp/hash/stencil/123/css/style-xyz.css'} | ||
| 46 | ] | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | 1 | const runner = testRunner({context, renderer}); | |
| 50 | 1 | runner([ | |
| 51 | { | ||
| 52 | input: '{{{stylesheet "assets/css/style.css" crossorigin="anonymous"}}} {{{stylesheet "assets/css/style.css" crossorigin="anonymous"}}}', | ||
| 53 | output: '<link data-stencil-stylesheet href="https://cdn.bcapp/hash/stencil/123/css/style-xyz.css" rel="stylesheet" crossorigin="anonymous"> <link data-stencil-stylesheet href="https://cdn.bcapp/hash/stencil/123/css/style-xyz.css" rel="stylesheet" crossorigin="anonymous">', | ||
| 54 | context | ||
| 55 | }, | ||
| 56 | ], () => { | ||
| 57 | const hints = renderer.getResourceHints(); | ||
| 58 | 1 | expect(hints.length).to.equals(1); | |
| 59 | 1 | hints.forEach(({src, type, state, cors}) => { | |
| 60 | expect(src).to.startWith(siteSettings.cdn_url); | ||
| 61 | 1 | expect(type).to.equals(resourceHintStyleType); | |
| 62 | 1 | expect(state).to.equals('preload'); | |
| 63 | 1 | expect(cors).to.equals(anonymousCors); | |
| 64 | }); | ||
| 65 | 1 | done(); | |
| 66 | }); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | it('should render a link tag and all extra attributes with no cdn url', done => { | |
| 70 | runTestCases([ | ||
| 71 | { | ||
| 72 | input: '{{{stylesheet "assets/css/style.css" blah rel="something" class="myStyle"}}}', | ||
| 73 | output: '<link data-stencil-stylesheet href="/assets/css/style.css" rel="something" class="myStyle">', | ||
| 74 | siteSettings: {}, | ||
| 75 | }, | ||
| 76 | ], done); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | it('should render a link with empty href and no resource hint', done => { | |
| 80 | const template = '{{{stylesheet "" }}}'; | ||
| 81 | |||
| 82 | 1 | const renderer = buildRenderer(siteSettings); | |
| 83 | 1 | renderer.renderString(template, {}) | |
| 84 | .then(r => { | ||
| 85 | expect(r).to.equals('<link data-stencil-stylesheet href="" rel="stylesheet">'); | ||
| 86 | 1 | const hints = renderer.getResourceHints(); | |
| 87 | 1 | expect(hints).to.have.length(0); | |
| 88 | 1 | done(); | |
| 89 | }) | ||
| 90 | .catch(done); | ||
| 91 | }); | ||
| 92 | |||
| 93 | 1 | it('should add configId to the filename', done => { | |
| 94 | const siteSettings = {theme_config_id: 'foo'}; | ||
| 95 | 1 | const renderer = buildRenderer(siteSettings); | |
| 96 | 1 | const runner = testRunner({renderer}); | |
| 97 | 1 | const src = '/assets/css/style-foo.css'; | |
| 98 | 1 | runner([ | |
| 99 | { | ||
| 100 | input: '{{{stylesheet "assets/css/style.css" }}}', | ||
| 101 | output: `<link data-stencil-stylesheet href="${src}" rel="stylesheet">`, | ||
| 102 | siteSettings: siteSettings, | ||
| 103 | }, | ||
| 104 | ], () => { | ||
| 105 | const hints = renderer.getResourceHints(); | ||
| 106 | 1 | expect(hints.length).to.equals(1); | |
| 107 | 1 | const hint = hints[0]; | |
| 108 | 1 | expect(hint.src).to.equals(src); | |
| 109 | 1 | expect(hint.state).to.equals('preload'); | |
| 110 | 1 | expect(hint.cors).to.equals(noCors); | |
| 111 | 1 | done(); | |
| 112 | }); | ||
| 113 | }); | ||
| 114 | |||
| 115 | 1 | it('should not append configId if the file is not in assets/css/ directory', done => { | |
| 116 | runTestCases([ | ||
| 117 | { | ||
| 118 | input: '{{{stylesheet "assets/lib/style.css" }}}', | ||
| 119 | output: '<link data-stencil-stylesheet href="/assets/lib/style.css" rel="stylesheet">', | ||
| 120 | siteSettings: { theme_config_id: 'foo' }, | ||
| 121 | }, | ||
| 122 | ], done); | ||
| 123 | }); | ||
| 124 | }); | ||
| 125 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('third party handlebars-helpers', function() { | |
| 8 | const context = { | ||
| 9 | array: [1, 2, 3, 4, 5], | ||
| 10 | null: null, | ||
| 11 | object: {"a" : 1, "b" : 2}, | ||
| 12 | options: { a: { b: { c: 'd' } } } | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | const runTestCases = testRunner({context}); | |
| 16 | |||
| 17 | 1 | describe('array helpers', function() { | |
| 18 | |||
| 19 | describe('after helper', function() { | ||
| 20 | it('returns all the items in an array after the index', function(done) { | ||
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{after array 1}}', | ||
| 24 | output: '2,3,4,5', | ||
| 25 | }, | ||
| 26 | ], done); | ||
| 27 | }); | ||
| 28 | }); | ||
| 29 | |||
| 30 | 1 | describe('first helper', function() { | |
| 31 | it('returns the first n items in an array', function(done) { | ||
| 32 | runTestCases([ | ||
| 33 | { | ||
| 34 | input: '{{first array 2}}', | ||
| 35 | output: '1,2', | ||
| 36 | }, | ||
| 37 | ], done); | ||
| 38 | }); | ||
| 39 | }); | ||
| 40 | |||
| 41 | }); | ||
| 42 | |||
| 43 | 1 | describe('collection helpers', function() { | |
| 44 | |||
| 45 | describe('length helper', function() { | ||
| 46 | it('returns the length of the array', function(done) { | ||
| 47 | runTestCases([ | ||
| 48 | { | ||
| 49 | input: '{{length array}}', | ||
| 50 | output: '5', | ||
| 51 | }, | ||
| 52 | ], done); | ||
| 53 | }); | ||
| 54 | }); | ||
| 55 | |||
| 56 | }); | ||
| 57 | |||
| 58 | 1 | describe('comparison helpers', function() { | |
| 59 | |||
| 60 | describe('contains helper', function() { | ||
| 61 | it('renders the contains block if it evaluates to true', function(done) { | ||
| 62 | runTestCases([ | ||
| 63 | { | ||
| 64 | input: `{{#contains array 1}}This will be rendered.{{else}}This will not be rendered.{{/contains}}`, | ||
| 65 | output: 'This will be rendered.', | ||
| 66 | }, | ||
| 67 | ], done); | ||
| 68 | }); | ||
| 69 | |||
| 70 | 1 | it('renders the else block if it evaluates to false', function(done) { | |
| 71 | runTestCases([ | ||
| 72 | { | ||
| 73 | input: `{{#contains array '1'}}This will not be rendered.{{else}}This will be rendered.{{/contains}}`, | ||
| 74 | output: 'This will be rendered.', | ||
| 75 | }, | ||
| 76 | ], done); | ||
| 77 | }); | ||
| 78 | 1 | it('renders the contains block if it evaluates to true. object as input', function(done) { | |
| 79 | runTestCases([ | ||
| 80 | { | ||
| 81 | input: `{{#contains object 1}}This will be rendered.{{else}}This will not be rendered.{{/contains}}`, | ||
| 82 | output: 'This will be rendered.', | ||
| 83 | }, | ||
| 84 | ], done); | ||
| 85 | }); | ||
| 86 | 1 | it('renders the else block if it evaluates to false. object as input', function(done) { | |
| 87 | runTestCases([ | ||
| 88 | { | ||
| 89 | input: `{{#contains object '3'}}This will not be rendered.{{else}}This will be rendered.{{/contains}}`, | ||
| 90 | output: 'This will be rendered.', | ||
| 91 | }, | ||
| 92 | ], done); | ||
| 93 | }); | ||
| 94 | 1 | it('renders the else block if it evaluates to false. null / number as input', function(done) { | |
| 95 | runTestCases([ | ||
| 96 | { | ||
| 97 | input: `{{#contains null '3'}}This will not be rendered.{{else}}This will be rendered.{{/contains}}`, | ||
| 98 | output: 'This will be rendered.', | ||
| 99 | }, | ||
| 100 | { | ||
| 101 | input: `{{#contains 111 '3'}}This will not be rendered.{{else}}This will be rendered.{{/contains}}`, | ||
| 102 | output: 'This will be rendered.', | ||
| 103 | }, | ||
| 104 | ], done); | ||
| 105 | }); | ||
| 106 | }); | ||
| 107 | |||
| 108 | }); | ||
| 109 | |||
| 110 | 1 | describe('html helpers', function() { | |
| 111 | |||
| 112 | describe('contains ellipsis', function() { | ||
| 113 | it('truncates a string to the specified length and appends an ellipsis', function(done) { | ||
| 114 | runTestCases([ | ||
| 115 | { | ||
| 116 | input: `{{ellipsis "<span>foo bar baz</span>" 7}}`, | ||
| 117 | output: 'foo bar…', | ||
| 118 | }, | ||
| 119 | ], done); | ||
| 120 | }); | ||
| 121 | }); | ||
| 122 | |||
| 123 | 1 | describe('contains thumbnailImage', function() { | |
| 124 | it('creates a <figure> with a thumbnail linked to an image', function(done) { | ||
| 125 | const context = { | ||
| 126 | data: { | ||
| 127 | id: 'id', | ||
| 128 | alt: 'alt', | ||
| 129 | thumbnail: 'thumbnail.png', | ||
| 130 | size: { | ||
| 131 | width: 32, | ||
| 132 | height: 32 | ||
| 133 | } | ||
| 134 | } | ||
| 135 | }; | ||
| 136 | |||
| 137 | 1 | runTestCases([ | |
| 138 | { | ||
| 139 | input: `{{{thumbnailImage data}}}`, | ||
| 140 | output: '<figure id=\"image-id\">\n<img alt=\"alt\" src=\"thumbnail.png\" width=\"32\" height=\"32\">\n</figure>', | ||
| 141 | context: context, | ||
| 142 | }, | ||
| 143 | ], done); | ||
| 144 | }); | ||
| 145 | }); | ||
| 146 | |||
| 147 | }); | ||
| 148 | |||
| 149 | 1 | describe('inflection helpers', function() { | |
| 150 | |||
| 151 | describe('contains ordinalize', function() { | ||
| 152 | it('returns an ordinalized number as a string', function(done) { | ||
| 153 | runTestCases([ | ||
| 154 | { | ||
| 155 | input: `{{ordinalize 42}}`, | ||
| 156 | output: '42nd', | ||
| 157 | }, | ||
| 158 | ], done); | ||
| 159 | }); | ||
| 160 | }); | ||
| 161 | |||
| 162 | }); | ||
| 163 | |||
| 164 | 1 | describe('markdown helpers', function() { | |
| 165 | |||
| 166 | describe('contains markdown', function() { | ||
| 167 | it('converts a string of markdown to HTML', function(done) { | ||
| 168 | runTestCases([ | ||
| 169 | { | ||
| 170 | input: `{{#markdown}}# Foo{{/markdown}}`, | ||
| 171 | output: '<h1>Foo</h1>\n', | ||
| 172 | }, | ||
| 173 | ], done); | ||
| 174 | }); | ||
| 175 | }); | ||
| 176 | |||
| 177 | }); | ||
| 178 | |||
| 179 | 1 | describe('math helpers', function() { | |
| 180 | |||
| 181 | describe('contains avg', function() { | ||
| 182 | it('returns the average of the numbers in an array', function(done) { | ||
| 183 | runTestCases([ | ||
| 184 | { | ||
| 185 | input: `{{avg array}}`, | ||
| 186 | output: '3', | ||
| 187 | }, | ||
| 188 | ], done); | ||
| 189 | }); | ||
| 190 | }); | ||
| 191 | |||
| 192 | }); | ||
| 193 | |||
| 194 | 1 | describe('object helpers', function() { | |
| 195 | |||
| 196 | describe('contains isObject', function() { | ||
| 197 | it('returns true if the value is an object', function(done) { | ||
| 198 | runTestCases([ | ||
| 199 | { | ||
| 200 | input: `{{isObject options}}`, | ||
| 201 | output: 'true', | ||
| 202 | }, | ||
| 203 | ], done); | ||
| 204 | }); | ||
| 205 | |||
| 206 | 1 | it('returns false if the value is not an object', function(done) { | |
| 207 | runTestCases([ | ||
| 208 | { | ||
| 209 | input: `{{isObject "foo"}}`, | ||
| 210 | output: 'false', | ||
| 211 | }, | ||
| 212 | ], done); | ||
| 213 | }); | ||
| 214 | }); | ||
| 215 | |||
| 216 | }); | ||
| 217 | |||
| 218 | 1 | describe('string helpers', function() { | |
| 219 | |||
| 220 | describe('contains capitalize', function() { | ||
| 221 | it('capitalizes the first word in a sentence', function(done) { | ||
| 222 | runTestCases([ | ||
| 223 | { | ||
| 224 | input: `{{capitalize "foo bar baz"}}`, | ||
| 225 | output: 'Foo bar baz', | ||
| 226 | }, | ||
| 227 | ], done); | ||
| 228 | }); | ||
| 229 | }); | ||
| 230 | |||
| 231 | }); | ||
| 232 | }); | ||
| 233 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('toLowerCase helper', function() { | |
| 8 | const context = { | ||
| 9 | string: "I Love PIZZA", | ||
| 10 | number: 365, | ||
| 11 | object: {}, | ||
| 12 | array: [1, 2, 3] | ||
| 13 | }; | ||
| 14 | |||
| 15 | 1 | const runTestCases = testRunner({context}); | |
| 16 | |||
| 17 | 1 | it('should convert string to lower case', function(done) { | |
| 18 | runTestCases([ | ||
| 19 | { | ||
| 20 | input: '{{toLowerCase string}}', | ||
| 21 | output: 'i love pizza', | ||
| 22 | }, | ||
| 23 | { | ||
| 24 | input: '{{toLowerCase "HELLO"}}', | ||
| 25 | output: 'hello', | ||
| 26 | }, | ||
| 27 | ], done); | ||
| 28 | }); | ||
| 29 | |||
| 30 | 1 | it('should properly handle values other than strings', function(done) { | |
| 31 | runTestCases([ | ||
| 32 | { | ||
| 33 | input: '{{toLowerCase number}}', | ||
| 34 | output: '365', | ||
| 35 | }, | ||
| 36 | { | ||
| 37 | input: '{{toLowerCase 5}}', | ||
| 38 | output: '5', | ||
| 39 | }, | ||
| 40 | { | ||
| 41 | input: '{{toLowerCase object}}', | ||
| 42 | output: '[object Object]', | ||
| 43 | }, | ||
| 44 | { | ||
| 45 | input: '{{toLowerCase array}}', | ||
| 46 | output: '1,2,3', | ||
| 47 | }, | ||
| 48 | ], done); | ||
| 49 | }); | ||
| 50 | }); | ||
| 51 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it, | ||
| 5 | testRunner = require('../spec-helpers').testRunner; | ||
| 6 | |||
| 7 | 1 | describe('truncate helper', function() { | |
| 8 | const context = { | ||
| 9 | chinese_string: '𠜎𠜱𠝹𠱓𠱸𠲖𠳏', | ||
| 10 | number: 2, | ||
| 11 | spanish_string: 'mañana', | ||
| 12 | string: 'hello world', | ||
| 13 | unicode_string: 'She ❤️️ this', | ||
| 14 | empty_string: '', | ||
| 15 | null: null, | ||
| 16 | }; | ||
| 17 | |||
| 18 | 1 | const runTestCases = testRunner({context}); | |
| 19 | |||
| 20 | 1 | it('should return the entire string if length is longer than the input string', function(done) { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{truncate string 15}}', | ||
| 24 | output: 'hello world', | ||
| 25 | }, | ||
| 26 | ], done); | ||
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should return the first length number of characters', function(done) { | |
| 30 | runTestCases([ | ||
| 31 | { | ||
| 32 | input: '{{truncate string 5}}', | ||
| 33 | output: 'hello', | ||
| 34 | }, | ||
| 35 | ], done); | ||
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it('should return the first argument, coerced to a string, if it is not a string', function(done) { | |
| 39 | runTestCases([ | ||
| 40 | { | ||
| 41 | input: '{{truncate number 5}}', | ||
| 42 | output: '2', | ||
| 43 | }, | ||
| 44 | ], done); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | it('should handle non-English strings', function(done) { | |
| 48 | runTestCases([ | ||
| 49 | { | ||
| 50 | input: '{{truncate spanish_string 3}}', | ||
| 51 | output: 'mañ', | ||
| 52 | }, | ||
| 53 | { | ||
| 54 | input: '{{truncate chinese_string 3}}', | ||
| 55 | output: '𠜎𠜱𠝹', | ||
| 56 | }, | ||
| 57 | ], done); | ||
| 58 | }); | ||
| 59 | |||
| 60 | 1 | it('should handle empty strings', function(done) { | |
| 61 | runTestCases([ | ||
| 62 | { | ||
| 63 | input: '{{truncate empty_string 5}}', | ||
| 64 | output: '', | ||
| 65 | }, | ||
| 66 | ], done); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | it('should handle null object', function(done) { | |
| 70 | runTestCases([ | ||
| 71 | { | ||
| 72 | input: '{{truncate null 5}}', | ||
| 73 | output: '', | ||
| 74 | }, | ||
| 75 | ], done); | ||
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('should handle unicode strings', function(done) { | |
| 79 | runTestCases([ | ||
| 80 | { | ||
| 81 | input: '{{truncate unicode_string 5}}', | ||
| 82 | output: 'She ❤️', | ||
| 83 | }, | ||
| 84 | ], done); | ||
| 85 | }); | ||
| 86 | }); | ||
| 87 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const { describe, it } = exports.lab = require('lab').script(); | |
| 2 | |||
| 3 | 1 | const { testRunner, renderString} = require('../spec-helpers'); | |
| 4 | |||
| 5 | 1 | describe('typeof inline', () => { | |
| 6 | const context = { | ||
| 7 | string: 'John Doe', | ||
| 8 | number: 123, | ||
| 9 | boolean: true, | ||
| 10 | nullValue: null, | ||
| 11 | undefinedValue: undefined, | ||
| 12 | objectValue: { name: 'John' }, | ||
| 13 | array: ['John', 'Doe'], | ||
| 14 | // eslint-disable-next-line no-undef | ||
| 15 | bigint: BigInt(12345678901234567890), | ||
| 16 | functionValue: () => {}, | ||
| 17 | }; | ||
| 18 | 1 | const runTestCases = testRunner({context}); | |
| 19 | |||
| 20 | 1 | it('should return string', done => { | |
| 21 | runTestCases([ | ||
| 22 | { | ||
| 23 | input: '{{typeof string}}', | ||
| 24 | output: 'string', | ||
| 25 | }, | ||
| 26 | ], done); | ||
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should return number', done => { | |
| 30 | runTestCases([ | ||
| 31 | { | ||
| 32 | input: '{{typeof number}}', | ||
| 33 | output: 'number', | ||
| 34 | }, | ||
| 35 | ], done); | ||
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it('should return boolean', done => { | |
| 39 | runTestCases([ | ||
| 40 | { | ||
| 41 | input: '{{typeof boolean}}', | ||
| 42 | output: 'boolean', | ||
| 43 | }, | ||
| 44 | ], done); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | it('should return object: null', done => { | |
| 48 | runTestCases([ | ||
| 49 | { | ||
| 50 | input: '{{typeof nullValue}}', | ||
| 51 | output: 'object', // Null is an object in JavaScript | ||
| 52 | }, | ||
| 53 | ], done); | ||
| 54 | }); | ||
| 55 | |||
| 56 | 1 | it('should return object: undefined', done => { | |
| 57 | runTestCases([ | ||
| 58 | { | ||
| 59 | input: '{{typeof undefinedValue}}', | ||
| 60 | output: 'undefined', | ||
| 61 | }, | ||
| 62 | ], done); | ||
| 63 | }); | ||
| 64 | |||
| 65 | 1 | it('should return object: object', done => { | |
| 66 | runTestCases([ | ||
| 67 | { | ||
| 68 | input: '{{typeof objectValue}}', | ||
| 69 | output: 'object', | ||
| 70 | }, | ||
| 71 | ], done); | ||
| 72 | }); | ||
| 73 | |||
| 74 | 1 | it('should return object: array', done => { | |
| 75 | runTestCases([ | ||
| 76 | { | ||
| 77 | input: '{{typeof array}}', | ||
| 78 | output: 'object', | ||
| 79 | }, | ||
| 80 | ], done); | ||
| 81 | }); | ||
| 82 | |||
| 83 | 1 | it('should return bigInt', done => { | |
| 84 | runTestCases([ | ||
| 85 | { | ||
| 86 | input: '{{typeof bigint}}', | ||
| 87 | output: 'bigint', | ||
| 88 | }, | ||
| 89 | ], done); | ||
| 90 | }); | ||
| 91 | |||
| 92 | 1 | it('should return function', done => { | |
| 93 | runTestCases([ | ||
| 94 | { | ||
| 95 | input: '{{typeof functionValue}}', | ||
| 96 | output: 'function', | ||
| 97 | }, | ||
| 98 | ], done); | ||
| 99 | }); | ||
| 100 | |||
| 101 | |||
| 102 | 1 | it('should work with output from another function', done => { | |
| 103 | runTestCases([ | ||
| 104 | { | ||
| 105 | input: '{{typeof (multiConcat string number)}}', | ||
| 106 | output: 'string', | ||
| 107 | }, | ||
| 108 | ], done); | ||
| 109 | }); | ||
| 110 | |||
| 111 | 1 | it('should not accept more than one value, throw a error', function(done) { | |
| 112 | renderString("{{typeof number string}}").catch(_ => { | ||
| 113 | done(); | ||
| 114 | }); | ||
| 115 | }); | ||
| 116 | |||
| 117 | 1 | it('should not work as a block helper, throw a error', function(done) { | |
| 118 | renderString("{{#typeof number}}").catch(_ => { | ||
| 119 | done(); | ||
| 120 | }); | ||
| 121 | }); | ||
| 122 | }); | ||
| 123 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * https://github.com/helpers/handlebars-helpers/blob/0.8.4/test/array.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 1 | const Code = require('code'); | |
| 6 | 1 | const Lab = require('lab'); | |
| 7 | 1 | const lab = exports.lab = Lab.script(); | |
| 8 | 1 | const expect = Code.expect; | |
| 9 | 1 | const it = lab.it; | |
| 10 | 1 | const describe = lab.describe | |
| 11 | |||
| 12 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 13 | 1 | const renderer = buildRenderer(); | |
| 14 | 1 | const hbs = renderer.handlebars; | |
| 15 | 1 | const helpers = require('../../../helpers/3p/array'); | |
| 16 | |||
| 17 | 1 | Object.keys(helpers).forEach(key => { | |
| 18 | hbs.registerHelper(key, helpers[key]); | ||
| 19 | }) | ||
| 20 | |||
| 21 | |||
| 22 | 1 | var context = { | |
| 23 | array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'], | ||
| 24 | notArray: null, | ||
| 25 | }; | ||
| 26 | |||
| 27 | 1 | describe('array', function() { | |
| 28 | describe('after', function() { | ||
| 29 | it('should return an empty string when undefined.', function(done) { | ||
| 30 | expect(hbs.compile('{{after}}')()).to.equal(''); | ||
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('should return all of the items in an array after the given index.', function(done) { | |
| 35 | var fn = hbs.compile('{{after array 5}}'); | ||
| 36 | 1 | expect(fn(context)).to.equal(['f', 'g', 'h'].toString()); | |
| 37 | 1 | done(); | |
| 38 | }); | ||
| 39 | |||
| 40 | 1 | it('should return all of the items in an array after the specified count.', function(done) { | |
| 41 | var fn = hbs.compile('{{after notArray 5}}'); | ||
| 42 | 1 | expect(fn(context)).to.equal(''); | |
| 43 | 1 | done(); | |
| 44 | }); | ||
| 45 | |||
| 46 | 1 | it('should return all of the items in an array after the specified count.', function(done) { | |
| 47 | var fn = hbs.compile('{{after array 5}}'); | ||
| 48 | 1 | expect(fn(context)).to.equal(['f', 'g', 'h'].toString()); | |
| 49 | 1 | done(); | |
| 50 | }); | ||
| 51 | }); | ||
| 52 | |||
| 53 | 1 | describe('arrayify', function() { | |
| 54 | it('should arrayify a value.', function(done) { | ||
| 55 | expect(hbs.compile('{{#each (arrayify .)}}{{.}}{{/each}}')('foo')).to.equal('foo'); | ||
| 56 | 1 | expect(hbs.compile('{{#each (arrayify .)}}{{.}}{{/each}}')(['foo'])).to.equal('foo'); | |
| 57 | 1 | done(); | |
| 58 | }); | ||
| 59 | }); | ||
| 60 | |||
| 61 | 1 | describe('before', function() { | |
| 62 | it('should return an empty string when undefined.', function(done) { | ||
| 63 | expect(hbs.compile('{{before}}')()).to.equal(''); | ||
| 64 | 1 | done(); | |
| 65 | }); | ||
| 66 | 1 | it('should return all of the items in an array before the given index.', function(done) { | |
| 67 | var fn = hbs.compile('{{before array 5}}'); | ||
| 68 | 1 | expect(fn(context)).to.equal(['a', 'b', 'c'].toString()); | |
| 69 | 1 | done(); | |
| 70 | }); | ||
| 71 | |||
| 72 | 1 | it('should return all of the items in an array before the specified count.', function(done) { | |
| 73 | var fn = hbs.compile('{{before array 5}}'); | ||
| 74 | 1 | expect(fn(context)).to.equal(['a', 'b', 'c'].toString()); | |
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | describe('each', function() { | |
| 80 | it('should use the key and value of each property in an object inside a block.', function(done) { | ||
| 81 | var fn = hbs.compile('{{#each obj}}{{@key}}: {{this}} {{/each}}'); | ||
| 82 | 1 | expect(fn({obj: {fry: 3, bender: 120 }})).to.equal('fry: 3 bender: 120 '); | |
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | }); | ||
| 86 | |||
| 87 | 1 | describe('eachIndex', function() { | |
| 88 | it('should render the block using the array and each item\'s index.', function(done) { | ||
| 89 | var fn = hbs.compile('{{#eachIndex array}} {{item}} is {{index}} {{/eachIndex}}'); | ||
| 90 | 1 | expect(fn(context)).to.equal(' a is 0 b is 1 c is 2 d is 3 e is 4 f is 5 g is 6 h is 7 '); | |
| 91 | 1 | done(); | |
| 92 | }); | ||
| 93 | |||
| 94 | 1 | it('should render the block when non array is passed returning empty string', function(done) { | |
| 95 | var fn = hbs.compile('{{#eachIndex notArray}} {{item}} is {{index}} {{/eachIndex}}'); | ||
| 96 | 1 | expect(fn(context)).to.equal(''); | |
| 97 | 1 | done(); | |
| 98 | }); | ||
| 99 | }); | ||
| 100 | |||
| 101 | 1 | describe('first', function() { | |
| 102 | describe('array', function () { | ||
| 103 | it('should return the first item in a collection.', function(done) { | ||
| 104 | var fn = hbs.compile('{{first foo}}'); | ||
| 105 | 1 | expect(fn({foo: ['a', 'b', 'c']})).to.equal('a'); | |
| 106 | 1 | done(); | |
| 107 | }); | ||
| 108 | |||
| 109 | 1 | it('should return an array with the first two items in a collection.', function(done) { | |
| 110 | var fn = hbs.compile('{{first foo 2}}'); | ||
| 111 | 1 | expect(fn({foo: ['a', 'b', 'c']})).to.equal(['a', 'b'].toString()); | |
| 112 | 1 | done(); | |
| 113 | }); | ||
| 114 | |||
| 115 | 1 | it('should return an empty string when undefined.', function(done) { | |
| 116 | expect(hbs.compile('{{first}}')()).to.equal(''); | ||
| 117 | 1 | done(); | |
| 118 | }); | ||
| 119 | |||
| 120 | 1 | it('should return the first item in an array.', function(done) { | |
| 121 | var fn = hbs.compile('{{first foo}}'); | ||
| 122 | 1 | expect(fn({foo: ['a', 'b', 'c']})).to.equal('a'); | |
| 123 | 1 | done(); | |
| 124 | }); | ||
| 125 | |||
| 126 | 1 | it('should return an array with the first two items in an array.', function(done) { | |
| 127 | var fn = hbs.compile('{{first foo 2}}'); | ||
| 128 | 1 | expect(fn({foo: ['a', 'b', 'c']})).to.equal(['a', 'b'].toString()); | |
| 129 | 1 | done(); | |
| 130 | }); | ||
| 131 | }); | ||
| 132 | |||
| 133 | 1 | describe('string', function () { | |
| 134 | const context = { | ||
| 135 | myString: 'BigCommerce', | ||
| 136 | emptyString: '', | ||
| 137 | smallString: 'abc' | ||
| 138 | }; | ||
| 139 | |||
| 140 | 1 | it('should return an empty string when empty string is provided.', function (done) { | |
| 141 | expect(hbs.compile('{{first emptyString}}')(context)).to.equal(''); | ||
| 142 | 1 | expect(hbs.compile('{{first emptyString 2}}')(context)).to.equal(''); | |
| 143 | 1 | done(); | |
| 144 | }); | ||
| 145 | |||
| 146 | 1 | it('should return the whole string when the number is big enough.', function (done) { | |
| 147 | expect(hbs.compile('{{first smallString 5}}')(context)).to.equal('abc'); | ||
| 148 | 1 | done(); | |
| 149 | }); | ||
| 150 | |||
| 151 | 1 | it('should return the expected substring.', function (done) { | |
| 152 | expect(hbs.compile('{{first myString 5}}')(context)).to.equal('BigCo'); | ||
| 153 | 1 | done(); | |
| 154 | }); | ||
| 155 | }); | ||
| 156 | }); | ||
| 157 | |||
| 158 | 1 | describe('filter', function() { | |
| 159 | it('should render the block if the given string is in the array.', function(done) { | ||
| 160 | var source = '{{#filter array "d"}}AAA{{else}}BBB{{/filter}}'; | ||
| 161 | 1 | expect(hbs.compile(source)(context)).to.equal('AAA'); | |
| 162 | 1 | done(); | |
| 163 | }); | ||
| 164 | |||
| 165 | 1 | it('should render the inverse block if the string is not in the array:', function(done) { | |
| 166 | var source = '{{#filter array "foo"}}AAA{{else}}BBB{{/filter}}'; | ||
| 167 | 1 | expect(hbs.compile(source)(context)).to.equal('BBB'); | |
| 168 | 1 | done(); | |
| 169 | }); | ||
| 170 | |||
| 171 | 1 | it('should render a block for each object that has a "first" property with the value "d".', function(done) { | |
| 172 | |||
| 173 | var ctx = { | ||
| 174 | collection: [ | ||
| 175 | {first: 'aaa', last: 'bbb'}, | ||
| 176 | {first: 'b'}, | ||
| 177 | {title: 'ccc', last: 'ddd'}, | ||
| 178 | {first: 'd'}, | ||
| 179 | {first: 'eee', last: 'fff'}, | ||
| 180 | {first: 'f'}, | ||
| 181 | {title: 'ggg', last: 'hhh'}, | ||
| 182 | {first: 'h'} | ||
| 183 | ] | ||
| 184 | }; | ||
| 185 | |||
| 186 | 1 | var source = '{{#filter collection "d" property="first"}}{{this.first}}{{else}}ZZZ{{/filter}}'; | |
| 187 | 1 | var fn = hbs.compile(source); | |
| 188 | 1 | expect(fn(ctx)).to.equal('d'); | |
| 189 | 1 | done(); | |
| 190 | }); | ||
| 191 | }); | ||
| 192 | |||
| 193 | 1 | describe('forEach', function() { | |
| 194 | it('should iterate over an array, exposing objects as context.', function(done) { | ||
| 195 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 196 | |||
| 197 | 1 | var fn = hbs.compile('{{#forEach arr}}{{name}}{{/forEach}}'); | |
| 198 | 1 | expect(fn({arr: arr})).to.equal('abc'); | |
| 199 | 1 | done(); | |
| 200 | }); | ||
| 201 | |||
| 202 | 1 | it('should expose `index`', function(done) { | |
| 203 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 204 | |||
| 205 | 1 | var fn = hbs.compile('{{#forEach arr}}{{index}}{{/forEach}}'); | |
| 206 | 1 | expect(fn({arr: arr})).to.equal('123'); | |
| 207 | 1 | done(); | |
| 208 | }); | ||
| 209 | |||
| 210 | 1 | it('should expose `total`', function(done) { | |
| 211 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 212 | |||
| 213 | 1 | var fn = hbs.compile('{{#forEach arr}}{{total}}{{/forEach}}'); | |
| 214 | 1 | expect(fn({arr: arr})).to.equal('333'); | |
| 215 | 1 | done(); | |
| 216 | }); | ||
| 217 | |||
| 218 | 1 | it('should expose `isFirst`', function(done) { | |
| 219 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 220 | |||
| 221 | 1 | var fn = hbs.compile('{{#forEach arr}}{{isFirst}}{{/forEach}}'); | |
| 222 | 1 | expect(fn({arr: arr})).to.equal('truefalsefalse'); | |
| 223 | 1 | done(); | |
| 224 | }); | ||
| 225 | |||
| 226 | 1 | it('should expose `isLast`', function(done) { | |
| 227 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 228 | |||
| 229 | 1 | var fn = hbs.compile('{{#forEach arr}}{{isLast}}{{/forEach}}'); | |
| 230 | 1 | expect(fn({arr: arr})).to.equal('falsefalsetrue'); | |
| 231 | 1 | done(); | |
| 232 | }); | ||
| 233 | |||
| 234 | 1 | it('should return empty block', function(done) { | |
| 235 | let arr = []; | ||
| 236 | 1 | var fn = hbs.compile('{{#forEach arr}}{{isLast}}{{/forEach}}'); | |
| 237 | 1 | expect(fn({arr})).to.equal(''); | |
| 238 | |||
| 239 | 1 | arr = null; | |
| 240 | 1 | var fn = hbs.compile('{{#forEach arr}}{{isLast}}{{/forEach}}'); | |
| 241 | 1 | expect(fn({arr})).to.equal(''); | |
| 242 | 1 | done(); | |
| 243 | }); | ||
| 244 | }); | ||
| 245 | |||
| 246 | 1 | describe('inArray', function() { | |
| 247 | it('should render the first block when a value exists in the array.', function(done) { | ||
| 248 | var fn = hbs.compile('{{#inArray array "d"}}AAA{{else}}BBB{{/inArray}}'); | ||
| 249 | 1 | expect(fn(context)).to.equal('AAA'); | |
| 250 | 1 | done(); | |
| 251 | }); | ||
| 252 | |||
| 253 | 1 | it('should render the inverse block when a value does not exist.', function(done) { | |
| 254 | var fn = hbs.compile('{{#inArray array "foo"}}AAA{{else}}BBB{{/inArray}}'); | ||
| 255 | 1 | expect(fn(context)).to.equal('BBB'); | |
| 256 | 1 | done(); | |
| 257 | }); | ||
| 258 | }); | ||
| 259 | |||
| 260 | 1 | describe('isArray', function() { | |
| 261 | it('should return true if the value is an array.', function(done) { | ||
| 262 | expect(hbs.compile('{{isArray "foo"}}')()).to.equal('false'); | ||
| 263 | 1 | expect(hbs.compile('{{isArray \'["foo"]\'}}')()).to.equal('false'); | |
| 264 | 1 | expect(hbs.compile('{{isArray foo}}')({foo: ['foo']})).to.equal('true'); | |
| 265 | 1 | expect(hbs.compile('{{isArray (arrayify "foo")}}')()).to.equal('true'); | |
| 266 | 1 | expect(hbs.compile('{{isArray (arrayify ["foo"])}}')()).to.equal('true'); | |
| 267 | 1 | done(); | |
| 268 | }); | ||
| 269 | }); | ||
| 270 | |||
| 271 | 1 | describe('last', function() { | |
| 272 | describe('array', function () { | ||
| 273 | it('should return an empty string when undefined.', function(done) { | ||
| 274 | expect(hbs.compile('{{last}}')()).to.equal(''); | ||
| 275 | 1 | done(); | |
| 276 | }); | ||
| 277 | |||
| 278 | 1 | it('should return the last item in an array.', function(done) { | |
| 279 | expect(hbs.compile('{{last array}}')(context)).to.equal('h'); | ||
| 280 | 1 | done(); | |
| 281 | }); | ||
| 282 | |||
| 283 | 1 | it('should return an array with the last two items in an array.', function(done) { | |
| 284 | expect(hbs.compile('{{last array 2}}')(context)).to.equal(['g', 'h'].toString()); | ||
| 285 | 1 | done(); | |
| 286 | }); | ||
| 287 | |||
| 288 | 1 | it('should return an empty array array if non array is passed', function(done) { | |
| 289 | expect(hbs.compile('{{last notArray 2}}')(context)).to.equal([].toString()); | ||
| 290 | 1 | done(); | |
| 291 | }); | ||
| 292 | }); | ||
| 293 | |||
| 294 | 1 | describe('string', function () { | |
| 295 | const context = { | ||
| 296 | myString: 'BigCommerce', | ||
| 297 | emptyString: '', | ||
| 298 | smallString: 'abc' | ||
| 299 | }; | ||
| 300 | |||
| 301 | 1 | it('should return an empty string when empty string is provided.', function (done) { | |
| 302 | expect(hbs.compile('{{last emptyString}}')(context)).to.equal(''); | ||
| 303 | 1 | expect(hbs.compile('{{last emptyString 2}}')(context)).to.equal(''); | |
| 304 | 1 | done(); | |
| 305 | }); | ||
| 306 | |||
| 307 | 1 | it('should return the whole string when the number is big enough.', function (done) { | |
| 308 | expect(hbs.compile('{{last smallString 5}}')(context)).to.equal('abc'); | ||
| 309 | 1 | done(); | |
| 310 | }); | ||
| 311 | |||
| 312 | 1 | it('should return the expected substring.', function (done) { | |
| 313 | expect(hbs.compile('{{last myString 5}}')(context)).to.equal('merce'); | ||
| 314 | 1 | done(); | |
| 315 | }); | ||
| 316 | }); | ||
| 317 | }); | ||
| 318 | |||
| 319 | 1 | describe('lengthEqual', function() { | |
| 320 | it('should render the first block if length is the given number', function(done) { | ||
| 321 | var fn = hbs.compile('{{#lengthEqual array 8}}AAA{{else}}BBB{{/lengthEqual}}'); | ||
| 322 | 1 | expect(fn(context)).to.equal('AAA'); | |
| 323 | 1 | done(); | |
| 324 | }); | ||
| 325 | |||
| 326 | 1 | it('should render the inverse block if length is not the given number', function(done) { | |
| 327 | var fn = hbs.compile('{{#lengthEqual array 3}}AAA{{else}}BBB{{/lengthEqual}}'); | ||
| 328 | 1 | expect(fn(context)).to.equal('BBB'); | |
| 329 | 1 | done(); | |
| 330 | }); | ||
| 331 | }); | ||
| 332 | |||
| 333 | 1 | describe('map', function() { | |
| 334 | it('should return an empty string when undefined.', function(done) { | ||
| 335 | expect(hbs.compile('{{map}}')()).to.equal(''); | ||
| 336 | 1 | done(); | |
| 337 | }); | ||
| 338 | |||
| 339 | 1 | it('should map the items in the array and return new values.', function(done) { | |
| 340 | var o = {array: ['a', 'b', 'c']}; | ||
| 341 | 1 | o.double = function(str) { | |
| 342 | return str + str; | ||
| 343 | }; | ||
| 344 | 1 | var fn = hbs.compile('{{map array double}}'); | |
| 345 | 1 | expect(fn(o)).to.equal('aa,bb,cc'); | |
| 346 | 1 | done(); | |
| 347 | }); | ||
| 348 | |||
| 349 | 1 | it('should work with a string value:', function(done) { | |
| 350 | var o = {}; | ||
| 351 | 1 | o.double = function(str) { | |
| 352 | return str + str; | ||
| 353 | }; | ||
| 354 | 1 | var fn = hbs.compile('{{map \'["a","b","c"]\' double}}'); | |
| 355 | 1 | expect(fn(o)).to.equal('aa,bb,cc'); | |
| 356 | 1 | done(); | |
| 357 | }); | ||
| 358 | |||
| 359 | 1 | it('should return an empty string when the array syntax is invalid:', function(done) { | |
| 360 | var fn = hbs.compile('{{map \'["b", "c", "a"\'}}'); | ||
| 361 | 1 | expect(fn(context)).to.equal(''); | |
| 362 | 1 | done(); | |
| 363 | }); | ||
| 364 | }); | ||
| 365 | |||
| 366 | 1 | describe('some', function() { | |
| 367 | it('should render the first block if the callback returns true', function(done) { | ||
| 368 | var ctx = {array: ['a', 'b', 'c']}; | ||
| 369 | 1 | ctx.isString = function(val) { | |
| 370 | return typeof val === 'string'; | ||
| 371 | }; | ||
| 372 | 1 | var fn = hbs.compile('{{#some array isString}}AAA{{else}}BBB{{/some}}'); | |
| 373 | 1 | expect(fn(ctx)).to.equal('AAA'); | |
| 374 | 1 | done(); | |
| 375 | }); | ||
| 376 | |||
| 377 | 1 | it('should render the inverse block if the array is undefined', function(done) { | |
| 378 | var fn = hbs.compile('{{#some array isString}}AAA{{else}}BBB{{/some}}'); | ||
| 379 | 1 | expect(fn()).to.equal('BBB'); | |
| 380 | 1 | done(); | |
| 381 | }); | ||
| 382 | |||
| 383 | 1 | it('should render the inverse block if falsey', function(done) { | |
| 384 | var ctx = {array: [['a'], ['b'], ['c']]}; | ||
| 385 | 1 | ctx.isString = function(val) { | |
| 386 | return typeof val === 'string'; | ||
| 387 | }; | ||
| 388 | 1 | var fn = hbs.compile('{{#some array isString}}AAA{{else}}BBB{{/some}}'); | |
| 389 | 1 | expect(fn(ctx)).to.equal('BBB'); | |
| 390 | 1 | done(); | |
| 391 | }); | ||
| 392 | }); | ||
| 393 | |||
| 394 | 1 | describe('sort', function() { | |
| 395 | it('should return an empty string when an invalid value is passed:', function(done) { | ||
| 396 | var fn = hbs.compile('{{sort}}'); | ||
| 397 | 1 | var res = fn(); | |
| 398 | 1 | expect(res).to.equal(''); | |
| 399 | 1 | done(); | |
| 400 | }); | ||
| 401 | |||
| 402 | 1 | it('should sort the items in the array', function(done) { | |
| 403 | var fn = hbs.compile('{{sort array}}'); | ||
| 404 | 1 | var res = fn({array: ['c', 'a', 'b']}); | |
| 405 | 1 | expect(res).to.equal('a,b,c'); | |
| 406 | 1 | done(); | |
| 407 | }); | ||
| 408 | |||
| 409 | 1 | it('should return all items in an array sorted in lexicographical order.', function(done) { | |
| 410 | var fn = hbs.compile('{{sort array}}'); | ||
| 411 | 1 | expect(fn(context)).to.equal(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].toString()); | |
| 412 | 1 | done(); | |
| 413 | }); | ||
| 414 | |||
| 415 | 1 | it('should sort the items in the array in reverse order:', function(done) { | |
| 416 | var fn = hbs.compile('{{sort array reverse="true"}}'); | ||
| 417 | 1 | var res = fn({array: ['c', 'a', 'b']}); | |
| 418 | 1 | expect(res).to.equal('c,b,a'); | |
| 419 | 1 | done(); | |
| 420 | }); | ||
| 421 | }); | ||
| 422 | |||
| 423 | 1 | describe('sortBy', function() { | |
| 424 | it('should return an empty string when undefined.', function(done) { | ||
| 425 | expect(hbs.compile('{{sortBy}}')()).to.equal(''); | ||
| 426 | 1 | done(); | |
| 427 | }); | ||
| 428 | |||
| 429 | 1 | it('should sort the items in an array.', function(done) { | |
| 430 | var fn = hbs.compile('{{sortBy \'["b", "c", "a"]\'}}'); | ||
| 431 | 1 | expect(fn(context)).to.equal('a,b,c'); | |
| 432 | 1 | done(); | |
| 433 | }); | ||
| 434 | |||
| 435 | 1 | it('should return an empty string when the array is invalid:', function(done) { | |
| 436 | var fn = hbs.compile('{{sortBy \'["b", "c", "a"\'}}'); | ||
| 437 | 1 | expect(fn(context)).to.equal(''); | |
| 438 | 1 | done(); | |
| 439 | }); | ||
| 440 | |||
| 441 | 1 | it('should take a compare function.', function(done) { | |
| 442 | var o = {}; | ||
| 443 | 1 | o.compare = function(a, b) { | |
| 444 | return b.localeCompare(a); | ||
| 445 | }; | ||
| 446 | 1 | var fn = hbs.compile('{{sortBy \'["b", "c", "a"]\' compare}}'); | |
| 447 | 1 | expect(fn(o)).to.equal('c,b,a'); | |
| 448 | 1 | done(); | |
| 449 | }); | ||
| 450 | |||
| 451 | 1 | it('should sort based on object key:', function(done) { | |
| 452 | var ctx = {arr: [{a: 'zzz'}, {a: 'aaa'}]}; | ||
| 453 | |||
| 454 | 1 | const helpers = require('../../../helpers/3p/object'); | |
| 455 | |||
| 456 | 1 | Object.keys(helpers).forEach(key => { | |
| 457 | hbs.registerHelper(key, helpers[key]); | ||
| 458 | }) | ||
| 459 | 1 | var fn = hbs.compile('{{{stringify (sortBy arr "a") 0}}}'); | |
| 460 | 1 | expect(fn(ctx)).to.equal('[{"a":"aaa"},{"a":"zzz"}]'); | |
| 461 | 1 | done(); | |
| 462 | }); | ||
| 463 | }); | ||
| 464 | |||
| 465 | 1 | describe('withAfter', function() { | |
| 466 | it('should use all of the items in an array after the specified count.', function(done) { | ||
| 467 | var fn = hbs.compile('{{#withAfter array 5}}<{{this}}>{{/withAfter}}'); | ||
| 468 | 1 | expect(fn(context)).to.equal('<f><g><h>'); | |
| 469 | 1 | done(); | |
| 470 | }); | ||
| 471 | }); | ||
| 472 | |||
| 473 | 1 | describe('withBefore', function() { | |
| 474 | it('should use all of the items in an array before the specified count.', function(done) { | ||
| 475 | var fn = hbs.compile('{{#withBefore array 5}}<{{this}}>{{/withBefore}}'); | ||
| 476 | 1 | expect(fn(context)).to.equal('<a><b><c>'); | |
| 477 | 1 | done(); | |
| 478 | }); | ||
| 479 | }); | ||
| 480 | |||
| 481 | 1 | describe('withFirst', function() { | |
| 482 | it('should use the first item in an array.', function(done) { | ||
| 483 | var fn = hbs.compile('{{#withFirst array}}{{this}} is smart.{{/withFirst}}'); | ||
| 484 | 1 | expect(fn(context)).to.equal('a is smart.'); | |
| 485 | 1 | done(); | |
| 486 | }); | ||
| 487 | 1 | it('should return an empty string when no array is passed:', function(done) { | |
| 488 | expect(hbs.compile('{{#withFirst}}{{/withFirst}}')()).to.equal(''); | ||
| 489 | 1 | done(); | |
| 490 | }); | ||
| 491 | 1 | it('should return an empty string when no array is passed:', function(done) { | |
| 492 | const customContext = { array: null }; | ||
| 493 | 1 | var fn = hbs.compile('{{#withFirst array}}{{/withFirst}}'); | |
| 494 | 1 | expect(fn(customContext)).to.equal(''); | |
| 495 | 1 | done(); | |
| 496 | }); | ||
| 497 | 1 | it('should use the first two items in an array.', function(done) { | |
| 498 | var fn = hbs.compile('{{#withFirst array 2}}{{this}} is smart.{{/withFirst}}'); | ||
| 499 | 1 | expect(fn(context)).to.equal('a is smart.b is smart.'); | |
| 500 | 1 | done(); | |
| 501 | }); | ||
| 502 | }); | ||
| 503 | |||
| 504 | 1 | describe('withLast', function() { | |
| 505 | it('should return an empty string when undefined.', function(done) { | ||
| 506 | expect(hbs.compile('{{withLast}}')()).to.equal(''); | ||
| 507 | 1 | done(); | |
| 508 | }); | ||
| 509 | 1 | it('should use the last item in an array.', function(done) { | |
| 510 | var fn = hbs.compile('{{#withLast array}}{{this}} is dumb.{{/withLast}}'); | ||
| 511 | 1 | expect(fn(context)).to.equal('h is dumb.'); | |
| 512 | 1 | done(); | |
| 513 | }); | ||
| 514 | 1 | it('should use the last two items in an array.', function(done) { | |
| 515 | var fn = hbs.compile('{{#withLast array 2}}{{this}} is dumb.{{/withLast}}'); | ||
| 516 | 1 | expect(fn(context)).to.equal('g is dumb.h is dumb.'); | |
| 517 | 1 | done(); | |
| 518 | }); | ||
| 519 | }); | ||
| 520 | |||
| 521 | 1 | describe('withSort', function() { | |
| 522 | it('should return an empty string when array is undefined', function(done) { | ||
| 523 | var fn = hbs.compile('{{#withSort}}{{this}}{{/withSort}}'); | ||
| 524 | 1 | expect(fn(context)).to.equal(''); | |
| 525 | 1 | done(); | |
| 526 | }); | ||
| 527 | |||
| 528 | 1 | it('should sort the array in lexicographical order', function(done) { | |
| 529 | var fn = hbs.compile('{{#withSort array}}{{this}}{{/withSort}}'); | ||
| 530 | 1 | expect(fn(context)).to.equal('abcdefgh'); | |
| 531 | 1 | done(); | |
| 532 | }); | ||
| 533 | |||
| 534 | 1 | it('should sort the array in reverse order', function(done) { | |
| 535 | var fn = hbs.compile('{{#withSort array reverse="true"}}{{this}}{{/withSort}}'); | ||
| 536 | 1 | expect(fn(context)).to.equal('hgfedcba'); | |
| 537 | 1 | done(); | |
| 538 | }); | ||
| 539 | |||
| 540 | 1 | it('should sort the array by deliveries', function(done) { | |
| 541 | var fn = hbs.compile('{{#withSort collection "deliveries"}}{{name}}: {{deliveries}} <br>{{/withSort}}'); | ||
| 542 | 1 | var res = fn({ | |
| 543 | collection: [ | ||
| 544 | {name: 'f', deliveries: 8021 }, | ||
| 545 | {name: 'b', deliveries: 239 }, | ||
| 546 | {name: 'd', deliveries: -12 } | ||
| 547 | ] | ||
| 548 | }); | ||
| 549 | 1 | expect(res).to.equal('d: -12 <br>b: 239 <br>f: 8021 <br>'); | |
| 550 | 1 | done(); | |
| 551 | }); | ||
| 552 | |||
| 553 | 1 | it('should sort the array by deliveries in reverse order', function(done) { | |
| 554 | var fn = hbs.compile('{{#withSort collection "deliveries" reverse="true"}}{{name}}: {{deliveries}} <br>{{/withSort}}'); | ||
| 555 | 1 | var res = fn({ | |
| 556 | collection: [ | ||
| 557 | {name: 'f', deliveries: 8021 }, | ||
| 558 | {name: 'b', deliveries: 239 }, | ||
| 559 | {name: 'd', deliveries: -12 } | ||
| 560 | ] | ||
| 561 | }); | ||
| 562 | 1 | expect(res).to.equal('f: 8021 <br>b: 239 <br>d: -12 <br>'); | |
| 563 | 1 | done(); | |
| 564 | }); | ||
| 565 | }); | ||
| 566 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/collection'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | 1 | var context = {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']}; | |
| 18 | |||
| 19 | 1 | describe('collection', function() { | |
| 20 | describe('isEmpty', function() { | ||
| 21 | it('should render the first block when an array is empty.', function(done) { | ||
| 22 | var fn = hbs.compile('{{#isEmpty array}}AAA{{else}}BBB{{/isEmpty}}'); | ||
| 23 | 1 | expect(fn({array: []})).to.equal('AAA'); | |
| 24 | 1 | done(); | |
| 25 | }); | ||
| 26 | |||
| 27 | 1 | it('should render the first block when the value is null.', function(done) { | |
| 28 | var fn = hbs.compile('{{#isEmpty}}AAA{{else}}BBB{{/isEmpty}}'); | ||
| 29 | 1 | expect(fn({array: []})).to.equal('AAA'); | |
| 30 | 1 | done(); | |
| 31 | }); | ||
| 32 | |||
| 33 | 1 | it('should render the second block when an array is not empty.', function(done) { | |
| 34 | var fn = hbs.compile('{{#isEmpty array}}AAA{{else}}BBB{{/isEmpty}}'); | ||
| 35 | 1 | expect(fn(context)).to.equal('BBB'); | |
| 36 | 1 | done(); | |
| 37 | }); | ||
| 38 | |||
| 39 | 1 | it('should render the second block when an object is not empty.', function(done) { | |
| 40 | var fn = hbs.compile('{{#isEmpty object}}AAA{{else}}BBB{{/isEmpty}}'); | ||
| 41 | 1 | expect(fn({object: {foo: 'bar'}})).to.equal('BBB'); | |
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('should render the first block when an object is empty.', function(done) { | |
| 46 | var fn = hbs.compile('{{#isEmpty object}}AAA{{else}}BBB{{/isEmpty}}'); | ||
| 47 | 1 | expect(fn({object: {}})).to.equal('AAA'); | |
| 48 | 1 | done(); | |
| 49 | }); | ||
| 50 | }); | ||
| 51 | |||
| 52 | 1 | describe('iterate', function() { | |
| 53 | describe('object', function() { | ||
| 54 | it('should iterate over a plain object:', function(done) { | ||
| 55 | var obj = {a: 'aaa', b: 'bbb', c: 'ccc'}; | ||
| 56 | |||
| 57 | 1 | var fn = hbs.compile('{{#iterate obj}}{{.}}{{/iterate}}'); | |
| 58 | 1 | expect(fn({obj: obj})).to.equal('aaabbbccc'); | |
| 59 | 1 | done(); | |
| 60 | }); | ||
| 61 | |||
| 62 | 1 | it('should expose `@key`:', function(done) { | |
| 63 | var obj = {a: 'aaa', b: 'bbb', c: 'ccc'}; | ||
| 64 | |||
| 65 | 1 | var fn = hbs.compile('{{#iterate obj}}{{@key}}{{/iterate}}'); | |
| 66 | 1 | expect(fn({obj: obj})).to.equal('abc'); | |
| 67 | 1 | done(); | |
| 68 | }); | ||
| 69 | |||
| 70 | 1 | it('should render the inverse block when falsey:', function(done) { | |
| 71 | var fn = hbs.compile('{{#iterate obj}}A{{else}}B{{/iterate}}'); | ||
| 72 | 1 | expect(fn()).to.equal('B'); | |
| 73 | 1 | done(); | |
| 74 | }); | ||
| 75 | }); | ||
| 76 | |||
| 77 | 1 | describe('array', function() { | |
| 78 | it('should iterate over an array:', function(done) { | ||
| 79 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 80 | |||
| 81 | 1 | var fn = hbs.compile('{{#iterate arr}}{{name}}{{/iterate}}'); | |
| 82 | 1 | expect(fn({arr: arr})).to.equal('abc'); | |
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | |||
| 86 | 1 | it('should expose `@index`:', function(done) { | |
| 87 | var arr = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; | ||
| 88 | |||
| 89 | 1 | var fn = hbs.compile('{{#iterate arr}}{{@index}}{{/iterate}}'); | |
| 90 | 1 | expect(fn({arr: arr})).to.equal('012'); | |
| 91 | 1 | done(); | |
| 92 | }); | ||
| 93 | }); | ||
| 94 | }); | ||
| 95 | |||
| 96 | 1 | describe('length', function() { | |
| 97 | it('should return the length of the array', function(done) { | ||
| 98 | var fn = hbs.compile('{{length array}}'); | ||
| 99 | 1 | expect(fn(context)).to.equal('8'); | |
| 100 | 1 | done(); | |
| 101 | }); | ||
| 102 | |||
| 103 | 1 | it('should return an empty string when undefined.', function(done) { | |
| 104 | expect(hbs.compile('{{length}}')()).to.equal(''); | ||
| 105 | 1 | done(); | |
| 106 | }); | ||
| 107 | |||
| 108 | 1 | it('should return empty strig when the value is null.', function(done) { | |
| 109 | var fn = hbs.compile('{{length foo}}'); | ||
| 110 | 1 | expect(fn({foo: null})).to.equal(''); | |
| 111 | 1 | done(); | |
| 112 | }); | ||
| 113 | |||
| 114 | 1 | it('should return the length of a string.', function(done) { | |
| 115 | var fn = hbs.compile('{{length "foo"}}'); | ||
| 116 | 1 | expect(fn(context)).to.equal('3'); | |
| 117 | 1 | done(); | |
| 118 | }); | ||
| 119 | |||
| 120 | 1 | it('should parse an array passed as a string', function(done) { | |
| 121 | var fn = hbs.compile('{{length \'["b", "c", "a"]\'}}'); | ||
| 122 | 1 | expect(fn(context)).to.equal('3'); | |
| 123 | 1 | done(); | |
| 124 | }); | ||
| 125 | |||
| 126 | 1 | it('should return 0 when the array is invalid:', function(done) { | |
| 127 | var fn = hbs.compile('{{length \'["b", "c", "a"\'}}'); | ||
| 128 | 1 | expect(fn(context)).to.equal('0'); | |
| 129 | 1 | done(); | |
| 130 | }); | ||
| 131 | |||
| 132 | 1 | it('should return 0 when the value is not an array:', function(done) { | |
| 133 | var fn = hbs.compile('{{length foo}}'); | ||
| 134 | 1 | expect(fn({foo: {}})).to.equal('0'); | |
| 135 | 1 | done(); | |
| 136 | }); | ||
| 137 | }); | ||
| 138 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/comparison'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | 1 | describe('comparison', function() { | |
| 18 | describe('and', function() { | ||
| 19 | it('should render a block if both values are truthy.', function(done) { | ||
| 20 | var fn = hbs.compile('{{#and great magnificent}}A{{else}}B{{/and}}'); | ||
| 21 | 1 | expect(fn({great: true, magnificent: true})).to.equal('A'); | |
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should render the inverse block if both values are not truthy.', function(done) { | |
| 26 | var fn = hbs.compile('{{#and great magnificent}}A{{else}}B{{/and}}'); | ||
| 27 | 1 | expect(fn({great: true, magnificent: false})).to.equal('B'); | |
| 28 | 1 | done(); | |
| 29 | }); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | describe('gt', function() { | |
| 33 | var fn = hbs.compile('{{#gt a b}}A{{else}}B{{/gt}}'); | ||
| 34 | |||
| 35 | 1 | describe('second arg', function() { | |
| 36 | it('should render the first block if true.', function(done) { | ||
| 37 | expect(fn({a: 20, b: 15})).to.equal('A'); | ||
| 38 | 1 | done(); | |
| 39 | }); | ||
| 40 | 1 | it('should render the second block if equal.', function(done) { | |
| 41 | expect(fn({a: 15, b: 15})).to.equal('B'); | ||
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | 1 | it('should render the second block if false.', function(done) { | |
| 45 | expect(fn({a: 14, b: 15})).to.equal('B'); | ||
| 46 | 1 | done(); | |
| 47 | }); | ||
| 48 | }); | ||
| 49 | |||
| 50 | 1 | describe('compare hash', function() { | |
| 51 | it('should not render a block if the value is not equal to a given number.', function(done) { | ||
| 52 | var fn = hbs.compile('{{#gt number compare=8}}A{{/gt}}'); | ||
| 53 | 1 | expect(fn({number: 5})).to.equal(''); | |
| 54 | 1 | done(); | |
| 55 | }); | ||
| 56 | 1 | it('should render a block if the value is greater than a given number.', function(done) { | |
| 57 | var fn = hbs.compile('{{#gt number compare=8}}A{{/gt}}'); | ||
| 58 | 1 | expect(fn({number: 10})).to.equal('A'); | |
| 59 | 1 | done(); | |
| 60 | }); | ||
| 61 | 1 | it('should not render a block if the value is less than a given number.', function(done) { | |
| 62 | var fn = hbs.compile('{{#gt number compare=8}}A{{/gt}}'); | ||
| 63 | 1 | expect(fn({number: 5})).to.equal(''); | |
| 64 | 1 | done(); | |
| 65 | }); | ||
| 66 | }); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | describe('gte', function() { | |
| 70 | describe('second argument', function() { | ||
| 71 | var fn = hbs.compile('{{#gte a b}}A{{else}}B{{/gte}}'); | ||
| 72 | |||
| 73 | 1 | it('should render the first block if true.', function(done) { | |
| 74 | expect(fn({a: 20, b: 15})).to.equal('A'); | ||
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | 1 | it('should render the first block if equal.', function(done) { | |
| 78 | expect(fn({a: 15, b: 15})).to.equal('A'); | ||
| 79 | 1 | done(); | |
| 80 | }); | ||
| 81 | 1 | it('should render the second block if false.', function(done) { | |
| 82 | expect(fn({a: 14, b: 15})).to.equal('B'); | ||
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | }); | ||
| 86 | |||
| 87 | 1 | describe('hash compare', function() { | |
| 88 | it('should render a block if the value is greater than a given number.', function(done) { | ||
| 89 | var fn = hbs.compile('{{#gte number compare=8}}A{{/gte}}'); | ||
| 90 | 1 | expect(fn({number: 12})).to.equal('A'); | |
| 91 | 1 | done(); | |
| 92 | }); | ||
| 93 | 1 | it('should render a block if the value is equal to a given number.', function(done) { | |
| 94 | var fn = hbs.compile('{{#gte number compare=8}}A{{/gte}}'); | ||
| 95 | 1 | expect(fn({number: 8})).to.equal('A'); | |
| 96 | 1 | done(); | |
| 97 | }); | ||
| 98 | 1 | it('should not render a block if the value is less than a given number.', function(done) { | |
| 99 | var fn = hbs.compile('{{#gte number compare=8}}A{{/gte}}'); | ||
| 100 | 1 | expect(fn({number: 5})).to.equal(''); | |
| 101 | 1 | done(); | |
| 102 | }); | ||
| 103 | }); | ||
| 104 | }); | ||
| 105 | |||
| 106 | 1 | describe('has', function() { | |
| 107 | it('should render a block if the condition is true.', function(done) { | ||
| 108 | var fn = hbs.compile('{{#has context "C"}}A{{else}}B{{/has}}'); | ||
| 109 | 1 | expect(fn({context: 'CCC'})).to.equal('A'); | |
| 110 | 1 | done(); | |
| 111 | }); | ||
| 112 | |||
| 113 | 1 | it('should render the inverse block if false.', function(done) { | |
| 114 | var fn = hbs.compile('{{#has context "zzz"}}A{{else}}B{{/has}}'); | ||
| 115 | 1 | expect(fn({context: 'CCC'})).to.equal('B'); | |
| 116 | 1 | done(); | |
| 117 | }); | ||
| 118 | |||
| 119 | 1 | it('should render the inverse block if value is undefined.', function(done) { | |
| 120 | var fn = hbs.compile('{{#has context}}A{{else}}B{{/has}}'); | ||
| 121 | 1 | expect(fn({context: 'CCC'})).to.equal('B'); | |
| 122 | 1 | done(); | |
| 123 | }); | ||
| 124 | |||
| 125 | 1 | it('should render the inverse block if context is undefined.', function(done) { | |
| 126 | var fn = hbs.compile('{{#has}}A{{else}}B{{/has}}'); | ||
| 127 | 1 | expect(fn({context: 'CCC'})).to.equal('B'); | |
| 128 | 1 | done(); | |
| 129 | }); | ||
| 130 | |||
| 131 | 1 | it('should work with arrays', function(done) { | |
| 132 | var fn = hbs.compile('{{#has array "a"}}A{{else}}B{{/has}}'); | ||
| 133 | 1 | expect(fn({array: ['a', 'b', 'c']})).to.equal('A'); | |
| 134 | 1 | done(); | |
| 135 | }); | ||
| 136 | |||
| 137 | 1 | it('should work with two strings', function(done) { | |
| 138 | var fn = hbs.compile('{{#has "abc" "a"}}A{{else}}B{{/has}}'); | ||
| 139 | 1 | expect(fn()).to.equal('A'); | |
| 140 | 1 | done(); | |
| 141 | }); | ||
| 142 | |||
| 143 | 1 | it('should return the inverse when the second string is not found', function(done) { | |
| 144 | var fn = hbs.compile('{{#has "abc" "z"}}A{{else}}B{{/has}}'); | ||
| 145 | 1 | expect(fn()).to.equal('B'); | |
| 146 | 1 | done(); | |
| 147 | }); | ||
| 148 | |||
| 149 | 1 | it('should work with object keys', function(done) { | |
| 150 | var fn = hbs.compile('{{#has object "a"}}A{{else}}B{{/has}}'); | ||
| 151 | 1 | expect(fn({object: {a: 'b'}})).to.equal('A'); | |
| 152 | 1 | done(); | |
| 153 | }); | ||
| 154 | }); | ||
| 155 | |||
| 156 | 1 | describe('eq', function() { | |
| 157 | it('should render a block if the value is equal to a given number.', function(done) { | ||
| 158 | var fn = hbs.compile('{{#eq number compare=8}}A{{/eq}}'); | ||
| 159 | 1 | expect(fn({number: 8})).to.equal('A'); | |
| 160 | 1 | done(); | |
| 161 | }); | ||
| 162 | |||
| 163 | 1 | it('should render the inverse block if falsey.', function(done) { | |
| 164 | var fn = hbs.compile('{{#eq number compare=8}}A{{else}}B{{/eq}}'); | ||
| 165 | 1 | expect(fn({number: 9})).to.equal('B'); | |
| 166 | 1 | done(); | |
| 167 | }); | ||
| 168 | |||
| 169 | 1 | it('should compare first and second args', function(done) { | |
| 170 | var fn = hbs.compile('{{#eq number 8}}A{{else}}B{{/eq}}'); | ||
| 171 | 1 | expect(fn({number: 9})).to.equal('B'); | |
| 172 | 1 | done(); | |
| 173 | }); | ||
| 174 | }); | ||
| 175 | |||
| 176 | 1 | describe('ifEven', function() { | |
| 177 | it('should render the block if the given value is an even number', function(done) { | ||
| 178 | var fn = hbs.compile('{{#ifEven number}}A{{else}}B{{/ifEven}}'); | ||
| 179 | 1 | expect(fn({number: 8})).to.equal('A'); | |
| 180 | 1 | done(); | |
| 181 | }); | ||
| 182 | |||
| 183 | 1 | it('should render the inverse block if the number is odd', function(done) { | |
| 184 | var fn = hbs.compile('{{#ifEven number}}A{{else}}B{{/ifEven}}'); | ||
| 185 | 1 | expect(fn({number: 9})).to.equal('B'); | |
| 186 | 1 | done(); | |
| 187 | }); | ||
| 188 | }); | ||
| 189 | |||
| 190 | 1 | describe('ifNth', function() { | |
| 191 | it('should render a custom class on even rows', function(done) { | ||
| 192 | var source = '{{#each items}}<div{{#ifNth 2 @index}}{{else}} class="row-alternate"{{/ifNth}}>{{name}}</div>{{/each}}'; | ||
| 193 | 1 | var fn = hbs.compile(source); | |
| 194 | 1 | var context = { | |
| 195 | items: [ | ||
| 196 | { name: 'Philip J. Fry' }, | ||
| 197 | { name: 'Turanga Leela' }, | ||
| 198 | { name: 'Bender Bending Rodriguez' }, | ||
| 199 | { name: 'Amy Wong' }, | ||
| 200 | { name: 'Hermes Conrad' } | ||
| 201 | ] | ||
| 202 | }; | ||
| 203 | 1 | expect(fn(context), [ | |
| 204 | '<div>Philip J. Fry</div>', | ||
| 205 | '<div class="row-alternate">Turanga Leela</div>', | ||
| 206 | '<div>Bender Bending Rodriguez</div>', | ||
| 207 | '<div class="row-alternate">Amy Wong</div>', | ||
| 208 | '<div>Hermes Conrad</div>' | ||
| 209 | ].join('')); | ||
| 210 | 1 | done(); | |
| 211 | }); | ||
| 212 | }); | ||
| 213 | |||
| 214 | 1 | describe('ifOdd', function() { | |
| 215 | it('should render the block if the given value is an even number', function(done) { | ||
| 216 | var fn = hbs.compile('{{#ifOdd number}}A{{else}}B{{/ifOdd}}'); | ||
| 217 | 1 | expect(fn({number: 9})).to.equal('A'); | |
| 218 | 1 | done(); | |
| 219 | }); | ||
| 220 | |||
| 221 | 1 | it('should render the inverse block if the number is odd', function(done) { | |
| 222 | var fn = hbs.compile('{{#ifOdd number}}A{{else}}B{{/ifOdd}}'); | ||
| 223 | 1 | expect(fn({number: 8})).to.equal('B') | |
| 224 | 1 | done(); | |
| 225 | }); | ||
| 226 | }); | ||
| 227 | |||
| 228 | 1 | describe('is', function() { | |
| 229 | it('should render a block if the condition is true.', function(done) { | ||
| 230 | var fn = hbs.compile('{{#is value "CCC"}}A{{else}}B{{/is}}'); | ||
| 231 | 1 | expect(fn({value: 'CCC'})).to.equal('A'); | |
| 232 | 1 | done(); | |
| 233 | }); | ||
| 234 | |||
| 235 | 1 | it('should use the `compare` arg on the options hash', function(done) { | |
| 236 | var fn = hbs.compile('{{#is value compare="CCC"}}A{{else}}B{{/is}}'); | ||
| 237 | 1 | expect(fn({value: 'CCC'})).to.equal('A'); | |
| 238 | 1 | done(); | |
| 239 | }); | ||
| 240 | |||
| 241 | 1 | it('should render the inverse if the condition is false', function(done) { | |
| 242 | var fn = hbs.compile('{{#is value "FOO"}}A{{else}}B{{/is}}'); | ||
| 243 | 1 | expect(fn({value: 'CCC'})).to.equal('B'); | |
| 244 | 1 | done(); | |
| 245 | }); | ||
| 246 | }); | ||
| 247 | |||
| 248 | 1 | describe('isnt', function() { | |
| 249 | it('should render a block if the condition is not true.', function(done) { | ||
| 250 | var fn = hbs.compile('{{#isnt number 2}}A{{else}}B{{/isnt}}'); | ||
| 251 | 1 | expect(fn({number: 3})).to.equal('A'); | |
| 252 | 1 | done(); | |
| 253 | }); | ||
| 254 | |||
| 255 | 1 | it('should use the `compare` arg on the options hash', function(done) { | |
| 256 | var fn = hbs.compile('{{#isnt value compare="CCC"}}A{{else}}B{{/isnt}}'); | ||
| 257 | 1 | expect(fn({value: 'CCC'})).to.equal('B'); | |
| 258 | 1 | done(); | |
| 259 | }); | ||
| 260 | |||
| 261 | 1 | it('should render the inverse if the condition is false', function(done) { | |
| 262 | var fn = hbs.compile('{{#isnt value "FOO"}}A{{else}}B{{/isnt}}'); | ||
| 263 | 1 | expect(fn({value: 'CCC'})).to.equal('A'); | |
| 264 | 1 | done(); | |
| 265 | }); | ||
| 266 | }); | ||
| 267 | |||
| 268 | 1 | describe('lt', function() { | |
| 269 | describe('second arg', function() { | ||
| 270 | var fn = hbs.compile('{{#lt a b}}A{{else}}B{{/lt}}'); | ||
| 271 | |||
| 272 | 1 | it('should render the first block if true.', function(done) { | |
| 273 | expect(fn({a: 14, b: 15})).to.equal('A'); | ||
| 274 | 1 | done(); | |
| 275 | }); | ||
| 276 | 1 | it('should render the second block if equal.', function(done) { | |
| 277 | expect(fn({a: 15, b: 15})).to.equal('B'); | ||
| 278 | 1 | done(); | |
| 279 | }); | ||
| 280 | 1 | it('should render the second block if false.', function(done) { | |
| 281 | expect(fn({a: 20, b: 15})).to.equal('B'); | ||
| 282 | 1 | done(); | |
| 283 | }); | ||
| 284 | }); | ||
| 285 | |||
| 286 | 1 | describe('compare hash', function() { | |
| 287 | it('should render a block if the value is less than a given number.', function(done) { | ||
| 288 | var fn = hbs.compile('{{#lt number compare=8}}A{{/lt}}'); | ||
| 289 | 1 | expect(fn({number: 5})).to.equal('A'); | |
| 290 | 1 | done(); | |
| 291 | }); | ||
| 292 | 1 | it('should not render a block if the value is greater than a given number.', function(done) { | |
| 293 | var fn = hbs.compile('{{#lt number compare=8}}A{{/lt}}'); | ||
| 294 | 1 | expect(fn({number: 42})).to.equal(''); | |
| 295 | 1 | done(); | |
| 296 | }); | ||
| 297 | }); | ||
| 298 | }); | ||
| 299 | |||
| 300 | 1 | describe('lte', function() { | |
| 301 | var fn = hbs.compile('{{#lte a b}}A{{else}}B{{/lte}}'); | ||
| 302 | |||
| 303 | 1 | describe('second arg', function() { | |
| 304 | it('should render the first block if true.', function(done) { | ||
| 305 | expect(fn({a: 14, b: 15})).to.equal('A'); | ||
| 306 | 1 | done(); | |
| 307 | }); | ||
| 308 | |||
| 309 | 1 | it('should render the first block if equal.', function(done) { | |
| 310 | expect(fn({a: 15, b: 15})).to.equal('A'); | ||
| 311 | 1 | done(); | |
| 312 | }); | ||
| 313 | |||
| 314 | 1 | it('should render the second block if false.', function(done) { | |
| 315 | expect(fn({a: 20, b: 15})).to.equal('B'); | ||
| 316 | 1 | done(); | |
| 317 | }); | ||
| 318 | }); | ||
| 319 | |||
| 320 | 1 | describe('compare hash', function() { | |
| 321 | it('should render a block if the value is less than a given number.', function(done) { | ||
| 322 | var fn = hbs.compile('{{#lte number compare=8}}A{{/lte}}'); | ||
| 323 | 1 | expect(fn({number: 1})).to.equal('A'); | |
| 324 | 1 | done(); | |
| 325 | }); | ||
| 326 | |||
| 327 | 1 | it('should render a block if the value is equal to a given number.', function(done) { | |
| 328 | var fn = hbs.compile('{{#lte number compare=8}}A{{/lte}}'); | ||
| 329 | 1 | expect(fn({number: 8})).to.equal('A'); | |
| 330 | 1 | done(); | |
| 331 | }); | ||
| 332 | |||
| 333 | 1 | it('should not render a block if the value is greater than a given number.', function(done) { | |
| 334 | var fn = hbs.compile('{{#lte number compare=8}}A{{/lte}}'); | ||
| 335 | 1 | expect(fn({number: 27})).to.equal(''); | |
| 336 | 1 | done(); | |
| 337 | }); | ||
| 338 | }); | ||
| 339 | }); | ||
| 340 | |||
| 341 | 1 | describe('neither', function() { | |
| 342 | it('should render a block if one of the values is truthy.', function(done) { | ||
| 343 | var fn = hbs.compile('{{#neither great magnificent}}A{{else}}B{{/neither}}'); | ||
| 344 | 1 | expect(fn({great: false, magnificent: false})).to.equal('A'); | |
| 345 | 1 | done(); | |
| 346 | }); | ||
| 347 | |||
| 348 | 1 | it('should render the inverse block if neither are true.', function(done) { | |
| 349 | var fn = hbs.compile('{{#neither great magnificent}}A{{else}}B{{/neither}}'); | ||
| 350 | 1 | expect(fn({great: true, magnificent: false})).to.equal('B'); | |
| 351 | 1 | done(); | |
| 352 | }); | ||
| 353 | }); | ||
| 354 | |||
| 355 | 1 | describe('unlessEq', function() { | |
| 356 | it('should render a block unless the value is equal to a given number.', function(done) { | ||
| 357 | var fn = hbs.compile('{{#unlessEq number compare=8}}A{{/unlessEq}}'); | ||
| 358 | 1 | expect(fn({number: 10})).to.equal('A'); | |
| 359 | 1 | done(); | |
| 360 | }); | ||
| 361 | 1 | it('should render a block unless the value is equal to a given number.', function(done) { | |
| 362 | var fn = hbs.compile('{{#unlessEq number compare=8}}A{{/unlessEq}}'); | ||
| 363 | 1 | expect(fn({number: 8})).to.equal(''); | |
| 364 | 1 | done(); | |
| 365 | }); | ||
| 366 | }); | ||
| 367 | |||
| 368 | 1 | describe('unlessGt', function() { | |
| 369 | it('should render a block unless the value is greater than a given number.', function(done) { | ||
| 370 | var fn = hbs.compile('{{#unlessGt number compare=8}}A{{/unlessGt}}'); | ||
| 371 | 1 | expect(fn({number: 5})).to.equal('A'); | |
| 372 | 1 | done(); | |
| 373 | }); | ||
| 374 | 1 | it('should render a block unless the value is greater than a given number.', function(done) { | |
| 375 | var fn = hbs.compile('{{#unlessGt number compare=8}}A{{/unlessGt}}'); | ||
| 376 | 1 | expect(fn({number: 10})).to.equal(''); | |
| 377 | 1 | done(); | |
| 378 | }); | ||
| 379 | }); | ||
| 380 | |||
| 381 | 1 | describe('unlessLt', function() { | |
| 382 | it('should render a block unless the value is less than a given number.', function(done) { | ||
| 383 | var fn = hbs.compile('{{#unlessLt number compare=8}}A{{/unlessLt}}'); | ||
| 384 | 1 | expect(fn({number: 10})).to.equal('A'); | |
| 385 | 1 | done(); | |
| 386 | }); | ||
| 387 | 1 | it('should render a block unless the value is less than a given number.', function(done) { | |
| 388 | var fn = hbs.compile('{{#unlessLt number compare=8}}A{{/unlessLt}}'); | ||
| 389 | 1 | expect(fn({number: 5})).to.equal(''); | |
| 390 | 1 | done(); | |
| 391 | }); | ||
| 392 | }); | ||
| 393 | |||
| 394 | 1 | describe('unlessGteq', function() { | |
| 395 | it('should render a block unless the value is greater than or equal to a given number.', function(done) { | ||
| 396 | var fn = hbs.compile('{{#unlessGteq number compare=8}}A{{/unlessGteq}}'); | ||
| 397 | 1 | expect(fn({number: 4})).to.equal('A'); | |
| 398 | 1 | done(); | |
| 399 | }); | ||
| 400 | 1 | it('should render a block unless the value is greater than or equal to a given number.', function(done) { | |
| 401 | var fn = hbs.compile('{{#unlessGteq number compare=8}}A{{/unlessGteq}}'); | ||
| 402 | 1 | expect(fn({number: 8})).to.equal(''); | |
| 403 | 1 | done(); | |
| 404 | }); | ||
| 405 | 1 | it('should not render a block unless the value is greater than or equal to a given number.', function(done) { | |
| 406 | var fn = hbs.compile('{{#unlessGteq number compare=8}}A{{/unlessGteq}}'); | ||
| 407 | 1 | expect(fn({number: 34})).to.equal(''); | |
| 408 | 1 | done(); | |
| 409 | }); | ||
| 410 | }); | ||
| 411 | |||
| 412 | 1 | describe('unlessLteq', function() { | |
| 413 | it('should render a block unless the value is less than or equal to a given number.', function(done) { | ||
| 414 | var fn = hbs.compile('{{#unlessLteq number compare=8}}A{{/unlessLteq}}'); | ||
| 415 | 1 | expect(fn({number: 10})).to.equal('A'); | |
| 416 | 1 | done(); | |
| 417 | }); | ||
| 418 | 1 | it('should render a block unless the value is less than or equal to a given number.', function(done) { | |
| 419 | var fn = hbs.compile('{{#unlessLteq number compare=8}}A{{/unlessLteq}}'); | ||
| 420 | 1 | expect(fn({number: 8})).to.equal(''); | |
| 421 | 1 | done(); | |
| 422 | }); | ||
| 423 | 1 | it('should not render a block unless the value is less than or equal to a given number.', function(done) { | |
| 424 | var fn = hbs.compile('{{#unlessLteq number compare=8}}A{{/unlessLteq}}'); | ||
| 425 | 1 | expect(fn({number: 4})).to.equal(''); | |
| 426 | 1 | done(); | |
| 427 | }); | ||
| 428 | }); | ||
| 429 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it; | ||
| 5 | 1 | const {testRunner} = require("../../spec-helpers"); | |
| 6 | |||
| 7 | 1 | describe('first', () => { | |
| 8 | describe('string', () => { | ||
| 9 | |||
| 10 | const context = { | ||
| 11 | smallString: 'abc', | ||
| 12 | empty: '', | ||
| 13 | myString: 'BigCommerce' | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | const runner = testRunner({context}); | |
| 17 | |||
| 18 | 1 | it('should extract the first char when no "n" is given.', done => { | |
| 19 | runner([{ | ||
| 20 | input: '{{first myString}}', | ||
| 21 | output: 'B' | ||
| 22 | }], done); | ||
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should return the whole string when "n" is bigger that string size', done => { | |
| 26 | runner([{ | ||
| 27 | input: '{{first smallString 5}}', | ||
| 28 | output: context.smallString | ||
| 29 | }], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should return the expected string', done => { | |
| 33 | runner([{ | ||
| 34 | input: '{{first myString 3}}', | ||
| 35 | output: 'Big' | ||
| 36 | }], done); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | it('should return empty string when empty string is provided', done => { | |
| 40 | runner([{ | ||
| 41 | input: '{{first empty 3}}', | ||
| 42 | output: '' | ||
| 43 | }], done); | ||
| 44 | }); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | describe('array', () => { | |
| 48 | |||
| 49 | const context = { | ||
| 50 | small: [1,2,3], | ||
| 51 | empty:[], | ||
| 52 | arrayYay: [1,2,3,4,5,6,7,8,9,0] | ||
| 53 | }; | ||
| 54 | |||
| 55 | 1 | const runner = testRunner({context}); | |
| 56 | |||
| 57 | 1 | it('should extract the first elem when no "n" is given.', done => { | |
| 58 | runner([{ | ||
| 59 | input: '{{first arrayYay}}', | ||
| 60 | output: '1' | ||
| 61 | }], done); | ||
| 62 | }); | ||
| 63 | |||
| 64 | 1 | it('should return the whole array when "n" is bigger that array size', done => { | |
| 65 | runner([{ | ||
| 66 | input: '{{first small 5}}', | ||
| 67 | output: context.small.join() | ||
| 68 | }], done); | ||
| 69 | }); | ||
| 70 | |||
| 71 | 1 | it('should return the expected elems', done => { | |
| 72 | runner([{ | ||
| 73 | input: '{{first arrayYay 3}}', | ||
| 74 | output: [1,2,3].join() | ||
| 75 | }], done); | ||
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('should return empty string when empty array is provided', done => { | |
| 79 | runner([{ | ||
| 80 | input: '{{first empty 3}}', | ||
| 81 | output: '' | ||
| 82 | }], done); | ||
| 83 | }); | ||
| 84 | }); | ||
| 85 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/html'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | |||
| 18 | 1 | var locals = {data: [{aaa: 'AAA', bbb: 'BBB'}, {aaa: 'CCC', bbb: 'DDD'}]}; | |
| 19 | |||
| 20 | 1 | describe('html', function() { | |
| 21 | describe('ellipsis', function() { | ||
| 22 | it('should return an empty string if undefined', function(done) { | ||
| 23 | var fn = hbs.compile('{{ellipsis}}'); | ||
| 24 | 1 | expect(fn()).to.equal(''); | |
| 25 | 1 | done(); | |
| 26 | }); | ||
| 27 | 1 | it('should return then string truncated by a specified length.', function(done) { | |
| 28 | var fn = hbs.compile('{{ellipsis "Bender should not be allowed on tv." 31}}'); | ||
| 29 | 1 | expect(fn()).to.equal('Bender should not be allowed on…'); | |
| 30 | 1 | done(); | |
| 31 | }); | ||
| 32 | 1 | it('should return the string if shorter than the specified length.', function(done) { | |
| 33 | var fn = hbs.compile('{{ellipsis "Bender should not be allowed on tv." 100}}'); | ||
| 34 | 1 | expect(fn()).to.equal('Bender should not be allowed on tv.'); | |
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | describe('sanitize', function() { | |
| 40 | it('should return an empty string when undefined.', function(done) { | ||
| 41 | expect(hbs.compile('{{sanitize}}')()).to.equal(''); | ||
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | 1 | it('should strip html from a string.', function(done) { | |
| 45 | var actual = hbs.compile('{{sanitize "<span>foo</span>"}}')(); | ||
| 46 | 1 | expect(actual).to.equal('foo'); | |
| 47 | 1 | done(); | |
| 48 | }); | ||
| 49 | }); | ||
| 50 | |||
| 51 | 1 | describe('ul', function() { | |
| 52 | it('should should return an unordered list', function(done) { | ||
| 53 | var fn = hbs.compile('{{#ul data class="names"}}{{aaa}} {{bbb}}{{/ul}}'); | ||
| 54 | 1 | expect(fn(locals)).to.equal('<ul class="names"><li>AAA BBB</li>\n<li>CCC DDD</li></ul>'); | |
| 55 | 1 | done(); | |
| 56 | }); | ||
| 57 | }); | ||
| 58 | |||
| 59 | 1 | describe('ol', function() { | |
| 60 | it('should should return an ordered list', function(done) { | ||
| 61 | var fn = hbs.compile('{{#ol data class="names"}}{{aaa}} {{bbb}}{{/ol}}'); | ||
| 62 | 1 | expect(fn(locals)).to.equal('<ol class="names"><li>AAA BBB</li>\n<li>CCC DDD</li></ol>'); | |
| 63 | 1 | done(); | |
| 64 | }); | ||
| 65 | }); | ||
| 66 | |||
| 67 | 1 | describe('thumbnailImage', function() { | |
| 68 | describe('{{{thumbnailImage context}}}', function() { | ||
| 69 | it('should return figure with link and caption', function(done) { | ||
| 70 | var context = { | ||
| 71 | data: { | ||
| 72 | id: 'id', | ||
| 73 | alt: 'Picture of a placeholder', | ||
| 74 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 75 | size: { | ||
| 76 | width: 200, | ||
| 77 | height: 200 | ||
| 78 | }, | ||
| 79 | full: 'http://placehold.it/600x400/0eafff/ffffff.png', | ||
| 80 | caption: 'My new caption!' | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | 1 | var fn = hbs.compile('{{{thumbnailImage data}}}'); | |
| 84 | 1 | var comparison = [ | |
| 85 | '<figure id="image-id">', | ||
| 86 | '<a href="http://placehold.it/600x400/0eafff/ffffff.png" rel="thumbnail">', | ||
| 87 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200">', | ||
| 88 | '</a>', | ||
| 89 | '<figcaption>My new caption!</figcaption>', | ||
| 90 | '</figure>', | ||
| 91 | ].join('\n'); | ||
| 92 | 1 | expect(fn(context)).to.equal(comparison); | |
| 93 | 1 | done(); | |
| 94 | }); | ||
| 95 | |||
| 96 | 1 | it('should return figure with extra class "test"', function(done) { | |
| 97 | var source = '{{{thumbnailImage data}}}'; | ||
| 98 | 1 | var context = { | |
| 99 | data: { | ||
| 100 | id: 'id', | ||
| 101 | alt: 'Picture of a placeholder', | ||
| 102 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 103 | size: { | ||
| 104 | width: 200, | ||
| 105 | height: 200 | ||
| 106 | }, | ||
| 107 | classes: { | ||
| 108 | figure: ['test'] | ||
| 109 | }, | ||
| 110 | full: 'http://placehold.it/600x400/0eafff/ffffff.png', | ||
| 111 | caption: 'My new caption!' | ||
| 112 | } | ||
| 113 | }; | ||
| 114 | |||
| 115 | 1 | var fn = hbs.compile(source); | |
| 116 | 1 | var comparison = [ | |
| 117 | '<figure id="image-id" class="test">', | ||
| 118 | '<a href="http://placehold.it/600x400/0eafff/ffffff.png" rel="thumbnail">', | ||
| 119 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200">', | ||
| 120 | '</a>', | ||
| 121 | '<figcaption>My new caption!</figcaption>', | ||
| 122 | '</figure>' | ||
| 123 | ].join('\n'); | ||
| 124 | 1 | expect(fn(context)).to.equal(comparison); | |
| 125 | 1 | done(); | |
| 126 | }); | ||
| 127 | |||
| 128 | 1 | it('should return figure with image that has class "test"', function(done) { | |
| 129 | var source = '{{{thumbnailImage data}}}'; | ||
| 130 | 1 | var context = { | |
| 131 | data: { | ||
| 132 | id: 'id', | ||
| 133 | alt: 'Picture of a placeholder', | ||
| 134 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 135 | size: { | ||
| 136 | width: 200, | ||
| 137 | height: 200 | ||
| 138 | }, | ||
| 139 | full: 'http://placehold.it/600x400/0eafff/ffffff.png', | ||
| 140 | classes: { | ||
| 141 | image: ['test'] | ||
| 142 | }, | ||
| 143 | caption: 'My new caption!' | ||
| 144 | } | ||
| 145 | }; | ||
| 146 | 1 | var fn = hbs.compile(source); | |
| 147 | 1 | var comparison = [ | |
| 148 | '<figure id="image-id">', | ||
| 149 | '<a href="http://placehold.it/600x400/0eafff/ffffff.png" rel="thumbnail">', | ||
| 150 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200" class="test">', | ||
| 151 | '</a>', | ||
| 152 | '<figcaption>My new caption!</figcaption>', | ||
| 153 | '</figure>' | ||
| 154 | ].join('\n'); | ||
| 155 | 1 | expect(fn(context)).to.equal(comparison); | |
| 156 | 1 | done(); | |
| 157 | }); | ||
| 158 | |||
| 159 | 1 | it('should return figure with link that has class "test"', function(done) { | |
| 160 | var source = '{{{thumbnailImage data}}}'; | ||
| 161 | 1 | var context = { | |
| 162 | data: { | ||
| 163 | id: 'id', | ||
| 164 | alt: 'Picture of a placeholder', | ||
| 165 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 166 | size: { | ||
| 167 | width: 200, | ||
| 168 | height: 200 | ||
| 169 | }, | ||
| 170 | full: 'http://placehold.it/600x400/0eafff/ffffff.png', | ||
| 171 | classes: { | ||
| 172 | link: ['test'] | ||
| 173 | }, | ||
| 174 | caption: 'My new caption!' | ||
| 175 | } | ||
| 176 | }; | ||
| 177 | 1 | var fn = hbs.compile(source); | |
| 178 | 1 | var comparison = [ | |
| 179 | '<figure id="image-id">', | ||
| 180 | '<a href="http://placehold.it/600x400/0eafff/ffffff.png" rel="thumbnail" class="test">', | ||
| 181 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200">', | ||
| 182 | '</a>', | ||
| 183 | '<figcaption>My new caption!</figcaption>', | ||
| 184 | '</figure>', | ||
| 185 | ].join('\n'); | ||
| 186 | 1 | expect(fn(context)).to.equal(comparison); | |
| 187 | 1 | done(); | |
| 188 | }); | ||
| 189 | |||
| 190 | 1 | it('should return figure without link', function(done) { | |
| 191 | var source = '{{{thumbnailImage data}}}'; | ||
| 192 | 1 | var context = { | |
| 193 | data: { | ||
| 194 | id: 'id', | ||
| 195 | alt: 'Picture of a placeholder', | ||
| 196 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 197 | size: { | ||
| 198 | width: 200, | ||
| 199 | height: 200 | ||
| 200 | }, | ||
| 201 | caption: 'My new caption!' | ||
| 202 | } | ||
| 203 | }; | ||
| 204 | 1 | var fn = hbs.compile(source); | |
| 205 | 1 | var comparison = [ | |
| 206 | '<figure id="image-id">', | ||
| 207 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200">', | ||
| 208 | '<figcaption>My new caption!</figcaption>', | ||
| 209 | '</figure>' | ||
| 210 | ].join('\n'); | ||
| 211 | 1 | expect(fn(context)).to.equal(comparison); | |
| 212 | 1 | done(); | |
| 213 | }); | ||
| 214 | |||
| 215 | 1 | it('should return figure without caption', function(done) { | |
| 216 | var source = '{{{thumbnailImage data}}}'; | ||
| 217 | 1 | var context = { | |
| 218 | data: { | ||
| 219 | id: 'id', | ||
| 220 | alt: 'Picture of a placeholder', | ||
| 221 | thumbnail: 'http://placehold.it/200x200/0eafff/ffffff.png', | ||
| 222 | size: { | ||
| 223 | width: 200, | ||
| 224 | height: 200 | ||
| 225 | }, | ||
| 226 | full: 'http://placehold.it/600x400/0eafff/ffffff.png' | ||
| 227 | } | ||
| 228 | }; | ||
| 229 | 1 | var fn = hbs.compile(source); | |
| 230 | 1 | var comparison = [ | |
| 231 | '<figure id="image-id">', | ||
| 232 | '<a href="http://placehold.it/600x400/0eafff/ffffff.png" rel="thumbnail">', | ||
| 233 | '<img alt="Picture of a placeholder" src="http://placehold.it/200x200/0eafff/ffffff.png" width="200" height="200">', | ||
| 234 | '</a>', | ||
| 235 | '</figure>' | ||
| 236 | ].join('\n'); | ||
| 237 | 1 | expect(fn(context)).to.equal(comparison); | |
| 238 | 1 | done(); | |
| 239 | }); | ||
| 240 | }); | ||
| 241 | }); | ||
| 242 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/inflection'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | 1 | describe('inflection', function() { | |
| 18 | describe('inflect', function() { | ||
| 19 | it('should return the plural or singular form of a word based on a value.', function(done) { | ||
| 20 | var template = hbs.compile('{{inflect mail "junk" "mail"}}'); | ||
| 21 | 1 | expect(template({mail: 3})).to.equal('mail'); | |
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should return the plural or singular form of a word based on a value and include the count.', function(done) { | |
| 26 | var template = hbs.compile('{{inflect messages "message" "messages" true}}'); | ||
| 27 | 1 | expect(template({messages: 1})).to.equal('1 message'); | |
| 28 | 1 | done(); | |
| 29 | }); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | describe('ordinalize', function() { | |
| 33 | it('should return an ordinalized string.', function(done) { | ||
| 34 | expect(hbs.compile('{{ordinalize 1}}')()).to.equal('1st'); | ||
| 35 | 1 | expect(hbs.compile('{{ordinalize 3}}')()).to.equal('3rd'); | |
| 36 | 1 | expect(hbs.compile('{{ordinalize 11}}')()).to.equal('11th'); | |
| 37 | 1 | expect(hbs.compile('{{ordinalize 21}}')()).to.equal('21st'); | |
| 38 | 1 | expect(hbs.compile('{{ordinalize 29}}')()).to.equal('29th'); | |
| 39 | 1 | expect(hbs.compile('{{ordinalize 22}}')()).to.equal('22nd'); | |
| 40 | 1 | done(); | |
| 41 | }); | ||
| 42 | }); | ||
| 43 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Lab = require('lab'), | |
| 2 | lab = exports.lab = Lab.script(), | ||
| 3 | describe = lab.experiment, | ||
| 4 | it = lab.it; | ||
| 5 | 1 | const {testRunner} = require("../../spec-helpers"); | |
| 6 | |||
| 7 | 1 | describe('last', () => { | |
| 8 | describe('string', () => { | ||
| 9 | |||
| 10 | const context = { | ||
| 11 | smallString: 'abc', | ||
| 12 | empty: '', | ||
| 13 | myString: 'BigCommerce' | ||
| 14 | }; | ||
| 15 | |||
| 16 | 1 | const runner = testRunner({context}); | |
| 17 | |||
| 18 | 1 | it('should extract the last char when no "n" is given.', done => { | |
| 19 | runner([{ | ||
| 20 | input: '{{last myString}}', | ||
| 21 | output: 'e' | ||
| 22 | }], done); | ||
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should return the whole string when "n" is bigger that string size', done => { | |
| 26 | runner([{ | ||
| 27 | input: '{{last smallString 5}}', | ||
| 28 | output: context.smallString | ||
| 29 | }], done); | ||
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should return the expected string', done => { | |
| 33 | runner([{ | ||
| 34 | input: '{{last myString 3}}', | ||
| 35 | output: 'rce' | ||
| 36 | }], done); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | it('should return empty string when empty string is provided', done => { | |
| 40 | runner([{ | ||
| 41 | input: '{{last empty 3}}', | ||
| 42 | output: '' | ||
| 43 | }], done); | ||
| 44 | }); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | describe('array', () => { | |
| 48 | |||
| 49 | const context = { | ||
| 50 | small: [1, 2, 3], | ||
| 51 | empty: [], | ||
| 52 | arrayYay: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] | ||
| 53 | }; | ||
| 54 | |||
| 55 | 1 | const runner = testRunner({context}); | |
| 56 | |||
| 57 | 1 | it('should extract the last elem when no "n" is given.', done => { | |
| 58 | runner([{ | ||
| 59 | input: '{{last arrayYay}}', | ||
| 60 | output: '0' | ||
| 61 | }], done); | ||
| 62 | }); | ||
| 63 | |||
| 64 | 1 | it('should return the whole array when "n" is bigger that array size', done => { | |
| 65 | runner([{ | ||
| 66 | input: '{{last small 5}}', | ||
| 67 | output: context.small.join() | ||
| 68 | }], done); | ||
| 69 | }); | ||
| 70 | |||
| 71 | 1 | it('should return the expected elems', done => { | |
| 72 | runner([{ | ||
| 73 | input: '{{last arrayYay 3}}', | ||
| 74 | output: [8, 9, 0].join() | ||
| 75 | }], done); | ||
| 76 | }); | ||
| 77 | |||
| 78 | 1 | it('should return empty string when empty array is provided', done => { | |
| 79 | runner([{ | ||
| 80 | input: '{{last empty 3}}', | ||
| 81 | output: '' | ||
| 82 | }], done); | ||
| 83 | }); | ||
| 84 | }); | ||
| 85 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/markdown'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | |||
| 18 | 1 | describe('markdown', function() { | |
| 19 | describe('markdown', function() { | ||
| 20 | it('should render markdown using the {{#markdown}} block helper', function(done) { | ||
| 21 | var template = hbs.compile('{{#markdown}}## {{../title}}{{/markdown}}'); | ||
| 22 | 1 | expect(template({title: 'Markdown Test'})).to.equal('<h2>Markdown Test</h2>\n'); | |
| 23 | 1 | done(); | |
| 24 | }); | ||
| 25 | }); | ||
| 26 | }); | ||
| 27 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/math'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | 1 | describe('math', function() { | |
| 17 | describe('add', function() { | ||
| 18 | it('should return the sum of two numbers.', function(done) { | ||
| 19 | var fn = hbs.compile('{{add value 5}}'); | ||
| 20 | 1 | expect(fn({value: 5})).to.equal('10'); | |
| 21 | 1 | done(); | |
| 22 | }); | ||
| 23 | }); | ||
| 24 | |||
| 25 | 1 | describe('average', function() { | |
| 26 | it('should return the average of a list of numbers:', function(done) { | ||
| 27 | var fn = hbs.compile('{{avg 1 2 3 4}}'); | ||
| 28 | 1 | expect(fn()).to.equal('2.5'); | |
| 29 | 1 | done(); | |
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should return the average of an array of numbers:', function(done) { | |
| 33 | var fn = hbs.compile('{{avg array}}'); | ||
| 34 | 1 | expect(fn({array: [1, 3, 6, 9]})).to.equal('4.75'); | |
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | describe('ceil', function() { | |
| 40 | it('should return the value rounded up to the nearest integer.', function(done) { | ||
| 41 | var fn = hbs.compile('{{ceil value}}'); | ||
| 42 | 1 | expect(fn({value: 5.6})).to.equal('6'); | |
| 43 | 1 | done(); | |
| 44 | }); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | describe('divide', function() { | |
| 48 | it('should return the division of two numbers.', function(done) { | ||
| 49 | var fn = hbs.compile('{{divide value 5}}'); | ||
| 50 | 1 | expect(fn({value: 5})).to.equal('1'); | |
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | describe('floor', function() { | |
| 56 | it('should return the value rounded down to the nearest integer.', function(done) { | ||
| 57 | var fn = hbs.compile('{{floor value}}'); | ||
| 58 | 1 | expect(fn({value: 5.6})).to.equal('5'); | |
| 59 | 1 | done(); | |
| 60 | }); | ||
| 61 | }); | ||
| 62 | |||
| 63 | 1 | describe('multiply', function() { | |
| 64 | it('should return the multiplication of two numbers.', function(done) { | ||
| 65 | var fn = hbs.compile('{{multiply value 5}}'); | ||
| 66 | 1 | expect(fn({value: 5})).to.equal('25'); | |
| 67 | 1 | done(); | |
| 68 | }); | ||
| 69 | }); | ||
| 70 | |||
| 71 | 1 | describe('round', function() { | |
| 72 | it('should return the value rounded to the nearest integer.', function(done) { | ||
| 73 | var fn = hbs.compile('{{round value}}'); | ||
| 74 | 1 | expect(fn({value: 5.69})).to.equal('6'); | |
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | describe('subtract', function() { | |
| 80 | it('should return the difference of two numbers.', function(done) { | ||
| 81 | var fn = hbs.compile('{{subtract value 5}}'); | ||
| 82 | 1 | expect(fn({value: 5})).to.equal('0'); | |
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | }); | ||
| 86 | |||
| 87 | 1 | describe('sum', function() { | |
| 88 | it('should return the sum of multiple numbers.', function(done) { | ||
| 89 | var fn = hbs.compile('{{sum value 67 80}}'); | ||
| 90 | 1 | expect(fn({value: 20})).to.equal('167'); | |
| 91 | 1 | done(); | |
| 92 | }); | ||
| 93 | 1 | it('should return the sum of multiple numbers.', function(done) { | |
| 94 | var fn = hbs.compile('{{sum 1 2 3}}'); | ||
| 95 | 1 | expect(fn()).to.equal('6'); | |
| 96 | 1 | done(); | |
| 97 | }); | ||
| 98 | 1 | it('should return the total sum of array.', function(done) { | |
| 99 | var fn = hbs.compile('{{sum value}}'); | ||
| 100 | 1 | expect(fn({value: [1, 2, 3]})).to.equal('6'); | |
| 101 | 1 | done(); | |
| 102 | }); | ||
| 103 | 1 | it('should return the total sum of array and numbers.', function(done) { | |
| 104 | var fn = hbs.compile('{{sum value 5}}'); | ||
| 105 | 1 | expect(fn({value: [1, 2, 3]})).to.equal('11'); | |
| 106 | 1 | done(); | |
| 107 | }); | ||
| 108 | }); | ||
| 109 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/misc'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | |||
| 18 | 1 | describe('misc', function() { | |
| 19 | describe('default', function() { | ||
| 20 | it('should use the given value:', function(done) { | ||
| 21 | expect(hbs.compile('{{default title "A"}}')({title: 'B'})).to.equal('B'); | ||
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | 1 | it('should fallback to the default value when no value exists', function(done) { | |
| 25 | expect(hbs.compile('{{default title "A"}}')({title: null})).to.equal('A'); | ||
| 26 | 1 | expect(hbs.compile('{{default title "A"}}')()).to.equal('A'); | |
| 27 | 1 | done(); | |
| 28 | }); | ||
| 29 | }); | ||
| 30 | |||
| 31 | 1 | describe('noop', function() { | |
| 32 | it('should be a noop', function(done) { | ||
| 33 | var fn = hbs.compile('{{#noop}}{{message}}{{/noop}}'); | ||
| 34 | 1 | expect(fn({message: 'This is a test'})).to.equal('This is a test'); | |
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | describe('withHash', function() { | |
| 40 | it('should return an empty sting', function(done) { | ||
| 41 | var fn = hbs.compile('{{#withHash}}{{message}}{{/withHash}}'); | ||
| 42 | 1 | var actual = fn({message: 'This is a test'}); | |
| 43 | 1 | expect(typeof actual).to.equal('string'); | |
| 44 | 1 | expect(actual).to.equal(''); | |
| 45 | 1 | done(); | |
| 46 | }); | ||
| 47 | 1 | it('should not blow up when no hash is defined.', function(done) { | |
| 48 | var fn = hbs.compile('{{#withHash}}{{/withHash}}'); | ||
| 49 | 1 | expect(fn()).to.equal(''); | |
| 50 | 1 | done(); | |
| 51 | }); | ||
| 52 | 1 | it('should return the inverse hash when defined and the value is falsy.', function(done) { | |
| 53 | var fn = hbs.compile('{{#withHash}}foo{{else}}bar{{/withHash}}'); | ||
| 54 | 1 | expect(fn()).to.equal('bar'); | |
| 55 | 1 | done(); | |
| 56 | }); | ||
| 57 | 1 | it('should return string from the newly created context', function(done) { | |
| 58 | var fn = hbs.compile('{{#withHash message="test"}}{{message}}{{/withHash}}'); | ||
| 59 | 1 | expect(fn({message: 'This is a test'})).to.equal('test'); | |
| 60 | 1 | done(); | |
| 61 | }); | ||
| 62 | 1 | it('should return string from the parent context', function(done) { | |
| 63 | var fn = hbs.compile('{{#withHash message=this.message}}{{message}}{{/withHash}}'); | ||
| 64 | 1 | expect(fn({message: 'This is a test'})).to.equal('This is a test'); | |
| 65 | 1 | done(); | |
| 66 | }); | ||
| 67 | 1 | it('should add two attributes to the new context', function(done) { | |
| 68 | var fn = hbs.compile('{{#withHash subject="Feedback" message="Hello!"}}{{subject}} - {{message}}{{/withHash}}'); | ||
| 69 | 1 | expect(fn({})).to.equal('Feedback - Hello!'); | |
| 70 | 1 | done(); | |
| 71 | }); | ||
| 72 | }); | ||
| 73 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | |||
| 12 | |||
| 13 | 1 | describe('number', function() { | |
| 14 | describe('phoneNumber', function() { | ||
| 15 | it('Format a phone number.', function(done) { | ||
| 16 | var fn = hbs.compile('{{phoneNumber value}}'); | ||
| 17 | 1 | expect(fn({value: '8005551212'})).to.equal('(800) 555-1212'); | |
| 18 | 1 | done(); | |
| 19 | }); | ||
| 20 | }); | ||
| 21 | |||
| 22 | 1 | describe('toFixed', function() { | |
| 23 | it('should return the value rounded to the nearest integer.', function(done) { | ||
| 24 | var fn = hbs.compile('{{toFixed value}}'); | ||
| 25 | 1 | expect(fn({value: 5.53231 })).to.equal('6'); | |
| 26 | 1 | done(); | |
| 27 | }); | ||
| 28 | 1 | it('should return the value rounded exactly n digits after the decimal place.', function(done) { | |
| 29 | var fn = hbs.compile('{{toFixed value 3}}'); | ||
| 30 | 1 | expect(fn({value: 5.53231 })).to.equal('5.532'); | |
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | }); | ||
| 34 | |||
| 35 | 1 | describe('toPrecision', function() { | |
| 36 | it('Returns the number in fixed-point or exponential notation rounded to n significant digits.', function(done) { | ||
| 37 | var fn = hbs.compile('{{toPrecision value}}'); | ||
| 38 | 1 | expect(fn({value: 555.322 })).to.equal('6e+2'); | |
| 39 | 1 | done(); | |
| 40 | }); | ||
| 41 | 1 | it('should return the value rounded exactly n digits after the decimal place.', function(done) { | |
| 42 | var fn = hbs.compile('{{toPrecision value 4}}'); | ||
| 43 | 1 | expect(fn({value: 555.322 })).to.equal('555.3'); | |
| 44 | 1 | done(); | |
| 45 | }); | ||
| 46 | }); | ||
| 47 | |||
| 48 | 1 | describe('toExponential', function() { | |
| 49 | it('should return the number in fixed-point or exponential notation rounded to n significant digits.', function(done) { | ||
| 50 | var fn = hbs.compile('{{toExponential value}}'); | ||
| 51 | 1 | expect(fn({value: 5 })).to.equal('5e+0'); | |
| 52 | 1 | done(); | |
| 53 | }); | ||
| 54 | 1 | it('should return the number in fixed-point or exponential notation rounded to exactly n significant digits.', function(done) { | |
| 55 | var fn = hbs.compile('{{toExponential value 5}}'); | ||
| 56 | 1 | expect(fn({value: 5 })).to.equal('5.00000e+0'); | |
| 57 | 1 | done(); | |
| 58 | }); | ||
| 59 | }); | ||
| 60 | |||
| 61 | 1 | describe('toInt', function() { | |
| 62 | it('should return an integer.', function(done) { | ||
| 63 | var fn = hbs.compile('{{toInt value}}'); | ||
| 64 | 1 | expect(fn({value: '3cc'})).to.equal('3'); | |
| 65 | 1 | done(); | |
| 66 | }); | ||
| 67 | }); | ||
| 68 | |||
| 69 | 1 | describe('toFloat', function() { | |
| 70 | it('should return a floating point number.', function(done) { | ||
| 71 | var fn = hbs.compile('{{toFloat value}}'); | ||
| 72 | 1 | expect(fn({value: '3.1cc'})).to.equal('3.1'); | |
| 73 | 1 | done(); | |
| 74 | }); | ||
| 75 | }); | ||
| 76 | |||
| 77 | 1 | describe('addCommas', function() { | |
| 78 | it('should add commas to a number.', function(done) { | ||
| 79 | var fn = hbs.compile('{{addCommas value}}'); | ||
| 80 | 1 | expect(fn({value: 2222222 })).to.equal('2,222,222'); | |
| 81 | 1 | done(); | |
| 82 | }); | ||
| 83 | }); | ||
| 84 | |||
| 85 | 1 | describe('toAbbr', function() { | |
| 86 | it('should abbreviate the given number.', function(done) { | ||
| 87 | var fn = hbs.compile('{{toAbbr number}}'); | ||
| 88 | 1 | expect(fn({number: 123456789 })).to.equal('123.46m'); | |
| 89 | 1 | done(); | |
| 90 | }); | ||
| 91 | |||
| 92 | 1 | it('should abbreviate a number with to the given decimal.', function(done) { | |
| 93 | var fn = hbs.compile('{{toAbbr number 3}}'); | ||
| 94 | 1 | expect(fn({number: 123456789 })).to.equal('123.457m'); | |
| 95 | 1 | done(); | |
| 96 | }); | ||
| 97 | |||
| 98 | 1 | it('should round up to the next increment', function(done) { | |
| 99 | var fn = hbs.compile('{{toAbbr number}}'); | ||
| 100 | 1 | expect(fn({number: 999 })).to.equal('1k'); | |
| 101 | 1 | done(); | |
| 102 | }); | ||
| 103 | |||
| 104 | 1 | it('should abbreviate a number based on a number and include decimal.', function(done) { | |
| 105 | expect(hbs.compile('{{toAbbr number 0}}')({number: 9999999 })).to.equal('10m'); | ||
| 106 | 1 | expect(hbs.compile('{{toAbbr number}}')({number: 1000000000 })).to.equal('1b'); | |
| 107 | 1 | expect(hbs.compile('{{toAbbr number}}')({number: 1000000000000 })).to.equal('1t'); | |
| 108 | 1 | expect(hbs.compile('{{toAbbr number}}')({number: 1000000000000000 })).to.equal('1q'); | |
| 109 | 1 | expect(hbs.compile('{{toAbbr number}}')({number: 99393999393 })).to.equal('99.39b'); | |
| 110 | 1 | done(); | |
| 111 | }); | ||
| 112 | }); | ||
| 113 | |||
| 114 | 1 | describe('random', function() { | |
| 115 | it('should return a random number between two values.', function(done) { | ||
| 116 | var fn = hbs.compile('{{random 5 10}}'); | ||
| 117 | 1 | expect(fn()).to.be.within(5,10); | |
| 118 | 1 | done(); | |
| 119 | }); | ||
| 120 | }); | ||
| 121 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/object'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | |||
| 18 | 1 | var context = {object: {a: 'b', c: 'd', e: 'f'}}; | |
| 19 | |||
| 20 | 1 | describe('object', function() { | |
| 21 | describe('extend', function() { | ||
| 22 | it('should extend multiple objects into one:', function(done) { | ||
| 23 | var fn = hbs.compile('{{{stringify (extend a d g)}}}'); | ||
| 24 | 1 | var actual = fn({a: {b: 'c'}, d: {e: 'f'}, g: {h: 'i'}}); | |
| 25 | 1 | expect(actual).to.equal('{"b":"c","e":"f","h":"i"}'); | |
| 26 | 1 | done(); | |
| 27 | }); | ||
| 28 | |||
| 29 | 1 | it('should work as a non-helper util:', function(done) { | |
| 30 | var actual = helpers.extend({a: {b: 'c'}}, {d: {e: 'f'}}, {g: {h: 'i'}}); | ||
| 31 | 1 | expect(actual).to.equal({ a: { b: 'c' }, d: { e: 'f' }, g: { h: 'i' } }); | |
| 32 | 1 | done(); | |
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should skip over sparse objects', function(done) { | |
| 36 | var actual = helpers.extend({a: {b: 'c'}}, null, {g: {h: 'i'}}); | ||
| 37 | 1 | expect(actual).to.equal({ a: { b: 'c' }, g: { h: 'i' } }); | |
| 38 | 1 | done(); | |
| 39 | }); | ||
| 40 | }); | ||
| 41 | |||
| 42 | 1 | describe('forIn', function() { | |
| 43 | it('should iterate over each property in an object:', function(done) { | ||
| 44 | var fn = hbs.compile('{{#forIn this}} {{@key}} {{.}} {{/forIn}}'); | ||
| 45 | 1 | expect(fn(context.object)).to.equal(' a b c d e f '); | |
| 46 | 1 | done(); | |
| 47 | }); | ||
| 48 | |||
| 49 | 1 | it('should return the inverse block if no object is passed:', function(done) { | |
| 50 | var fn = hbs.compile('{{#forIn}} {{.}} {{else}} Nada. {{/forIn}}'); | ||
| 51 | 1 | expect(fn(context.object)).to.equal(' Nada. '); | |
| 52 | 1 | done(); | |
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('should expose private variables:', function(done) { | |
| 56 | var fn = hbs.compile('{{#forIn this abc=object}} {{@abc.a}} {{/forIn}}'); | ||
| 57 | 1 | expect(fn(context)).to.equal(' b '); | |
| 58 | 1 | done(); | |
| 59 | }); | ||
| 60 | }); | ||
| 61 | |||
| 62 | 1 | describe('forOwn', function() { | |
| 63 | it('should iterate over each property in an object:', function(done) { | ||
| 64 | var fn = hbs.compile('{{#forOwn this}} {{@key}} {{.}} {{/forOwn}}'); | ||
| 65 | 1 | expect(fn(context.object)).to.equal(' a b c d e f '); | |
| 66 | 1 | done(); | |
| 67 | }); | ||
| 68 | |||
| 69 | 1 | it('should return the inverse block if no object is passed:', function(done) { | |
| 70 | var fn = hbs.compile('{{#forOwn}} {{.}} {{else}} Nada. {{/forOwn}}'); | ||
| 71 | 1 | expect(fn(context.object)).to.equal(' Nada. '); | |
| 72 | 1 | done(); | |
| 73 | }); | ||
| 74 | |||
| 75 | 1 | it('should only expose "own" keys:', function(done) { | |
| 76 | function Foo() { | ||
| 77 | 1 | this.a = 'b'; | |
| 78 | 1 | this.b = 'c'; | |
| 79 | } | ||
| 80 | 1 | Foo.prototype.c = 'd'; | |
| 81 | 1 | var fn = hbs.compile('{{#forOwn this}} {{.}} {{/forOwn}}'); | |
| 82 | 1 | expect(fn(new Foo())).to.equal(' b c '); | |
| 83 | 1 | done(); | |
| 84 | }); | ||
| 85 | |||
| 86 | 1 | it('should expose private variables:', function(done) { | |
| 87 | var fn = hbs.compile('{{#forOwn this abc=object}} {{@abc.c}} {{/forOwn}}'); | ||
| 88 | 1 | expect(fn(context)).to.equal(' d '); | |
| 89 | 1 | done(); | |
| 90 | }); | ||
| 91 | }); | ||
| 92 | |||
| 93 | 1 | describe('toPath', function() { | |
| 94 | it('should return a path from provided arguments', function(done) { | ||
| 95 | expect(hbs.compile('{{toPath "a" "b" "c"}}')()).to.equal('a.b.c'); | ||
| 96 | 1 | done(); | |
| 97 | }); | ||
| 98 | 1 | it('should return a path from calculated arguments', function (done) { | |
| 99 | var t = hbs.compile('{{toPath "a" (add 1 1) "b"}}')(); | ||
| 100 | 1 | expect(t).to.equal('a.2.b'); | |
| 101 | 1 | done(); | |
| 102 | }); | ||
| 103 | 1 | it('should return a `get` compatible path', function(done) { | |
| 104 | var fn = hbs.compile('{{get (toPath "a" (add 1 1) "j") this}}'); | ||
| 105 | 1 | expect(fn({a: [{b: 'c', d: 'e'},{f: 'g', h: 'i'}, {j: 'k', l: 'm'}]})).to.equal('k'); | |
| 106 | 1 | done(); | |
| 107 | }); | ||
| 108 | }); | ||
| 109 | |||
| 110 | 1 | describe('hasOwn', function() { | |
| 111 | function Foo() { | ||
| 112 | 2 | this.a = 'b'; | |
| 113 | 2 | this.b = 'c'; | |
| 114 | } | ||
| 115 | 1 | Foo.prototype.c = 'd'; | |
| 116 | |||
| 117 | 1 | it('should return true if object has own property:', function(done) { | |
| 118 | var fn = hbs.compile('{{hasOwn this "a"}}'); | ||
| 119 | 1 | expect(fn(new Foo())).to.equal('true'); | |
| 120 | 1 | done(); | |
| 121 | }); | ||
| 122 | |||
| 123 | 1 | it('should return false if object does not have own property:', function(done) { | |
| 124 | var fn = hbs.compile('{{hasOwn this "c"}}'); | ||
| 125 | 1 | expect(fn(new Foo())).to.equal('false'); | |
| 126 | 1 | done(); | |
| 127 | }); | ||
| 128 | }); | ||
| 129 | |||
| 130 | 1 | describe('isObject', function() { | |
| 131 | it('should return true if value is an object:', function(done) { | ||
| 132 | var fn = hbs.compile('{{isObject this}}'); | ||
| 133 | 1 | expect(fn({a: 'b'})).to.equal('true'); | |
| 134 | 1 | done(); | |
| 135 | }); | ||
| 136 | |||
| 137 | 1 | it('should return false if value is not an object:', function(done) { | |
| 138 | var fn = hbs.compile('{{isObject this}}'); | ||
| 139 | 1 | expect(fn('foo')).to.equal('false'); | |
| 140 | 1 | done(); | |
| 141 | }); | ||
| 142 | }); | ||
| 143 | |||
| 144 | 1 | describe('merge', function() { | |
| 145 | it('should deeply merge objects passed on the context:', function(done) { | ||
| 146 | var fn = hbs.compile('{{{stringify (merge a b c)}}}'); | ||
| 147 | 1 | var actual = fn({a: {one: 'two'}, b: {one: 'three'}, c: {two: 'four'}}); | |
| 148 | 1 | expect(actual).to.equal('{"one":"three","two":"four"}'); | |
| 149 | 1 | done(); | |
| 150 | }); | ||
| 151 | }); | ||
| 152 | |||
| 153 | 1 | describe('parseJSON', function() { | |
| 154 | it('should parse a JSON string:', function(done) { | ||
| 155 | var fn = hbs.compile('{{#JSONparse jsonString}}{{name}}{{/JSONparse}}'); | ||
| 156 | 1 | expect(fn({jsonString: "{\"name\": \"Fry\"}"})).to.equal('Fry'); | |
| 157 | 1 | done(); | |
| 158 | }); | ||
| 159 | }); | ||
| 160 | |||
| 161 | 1 | describe('stringify', function() { | |
| 162 | it('should stringify an object:', function(done) { | ||
| 163 | var fn = hbs.compile('{{{stringify data}}}'); | ||
| 164 | 1 | var res = fn({data: {name: "Halle", age: 4, userid: "Nicole"}}); | |
| 165 | 1 | expect(res).to.equal('{"name":"Halle","age":4,"userid":"Nicole"}'); | |
| 166 | 1 | done(); | |
| 167 | }); | ||
| 168 | }); | ||
| 169 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | |||
| 9 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 10 | 1 | const renderer = buildRenderer(); | |
| 11 | 1 | const hbs = renderer.handlebars; | |
| 12 | 1 | const helpers = require('../../../helpers/3p/string'); | |
| 13 | |||
| 14 | 1 | Object.keys(helpers).forEach(key => { | |
| 15 | hbs.registerHelper(key, helpers[key]); | ||
| 16 | }); | ||
| 17 | |||
| 18 | 1 | describe('string', function() { | |
| 19 | describe('camelcase', function() { | ||
| 20 | it('should return an empty string if undefined', function(done) { | ||
| 21 | var fn = hbs.compile('{{camelcase}}'); | ||
| 22 | 1 | expect(fn()).to.equal(''); | |
| 23 | 1 | done(); | |
| 24 | }); | ||
| 25 | 1 | it('should return the string in camelcase', function(done) { | |
| 26 | var fn = hbs.compile('{{camelcase "foo bar baz qux"}}'); | ||
| 27 | 1 | expect(fn()).to.equal('fooBarBazQux'); | |
| 28 | 1 | done(); | |
| 29 | }); | ||
| 30 | 1 | it('should lowercase a single character', function(done) { | |
| 31 | expect(hbs.compile('{{camelcase "f"}}')()).to.equal('f'); | ||
| 32 | 1 | expect(hbs.compile('{{camelcase "A"}}')()).to.equal('a'); | |
| 33 | 1 | done(); | |
| 34 | }); | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | describe('capitalize', function() { | |
| 38 | it('should return an empty string if undefined', function(done) { | ||
| 39 | var fn = hbs.compile('{{capitalize}}'); | ||
| 40 | 1 | expect(fn()).to.equal(''); | |
| 41 | 1 | done(); | |
| 42 | }); | ||
| 43 | 1 | it('should capitalize a word.', function(done) { | |
| 44 | var fn = hbs.compile('{{capitalize "foo"}}'); | ||
| 45 | 1 | expect(fn()).to.equal('Foo'); | |
| 46 | 1 | done(); | |
| 47 | }); | ||
| 48 | }); | ||
| 49 | |||
| 50 | 1 | describe('capitalizeAll', function() { | |
| 51 | it('should return an empty string if undefined', function(done) { | ||
| 52 | var fn = hbs.compile('{{capitalizeAll}}'); | ||
| 53 | 1 | expect(fn()).to.equal(''); | |
| 54 | 1 | done(); | |
| 55 | }); | ||
| 56 | 1 | it('should return the string with the every word capitalized.', function(done) { | |
| 57 | var fn = hbs.compile('{{capitalizeAll "bender should not bE allowed on tV"}}'); | ||
| 58 | 1 | expect(fn()).to.equal('Bender Should Not BE Allowed On TV'); | |
| 59 | 1 | done(); | |
| 60 | }); | ||
| 61 | }); | ||
| 62 | |||
| 63 | 1 | describe('center', function() { | |
| 64 | it('should return an empty string if undefined', function(done) { | ||
| 65 | var fn = hbs.compile('{{center}}'); | ||
| 66 | 1 | expect(fn()).to.equal(''); | |
| 67 | 1 | done(); | |
| 68 | }); | ||
| 69 | 1 | it('should return the string centered by using non-breaking spaces.', function(done) { | |
| 70 | var fn = hbs.compile('{{center "Bender should not be allowed on tv." 2}}'); | ||
| 71 | 1 | expect(fn()).to.equal('&nbsp;&nbsp;Bender should not be allowed on tv.&nbsp;&nbsp;'); | |
| 72 | 1 | done(); | |
| 73 | }); | ||
| 74 | }); | ||
| 75 | |||
| 76 | 1 | describe('chop', function() { | |
| 77 | it('should return an empty string if undefined', function(done) { | ||
| 78 | var fn = hbs.compile('{{chop}}'); | ||
| 79 | 1 | expect(fn()).to.equal(''); | |
| 80 | 1 | done(); | |
| 81 | }); | ||
| 82 | 1 | it('should remove non-word characters from start of string', function(done) { | |
| 83 | var fn = hbs.compile('{{chop "- foo bar baz"}}'); | ||
| 84 | 1 | expect(fn()).to.equal('foo bar baz'); | |
| 85 | 1 | done(); | |
| 86 | }); | ||
| 87 | 1 | it('should remove non-word characters from end of string', function(done) { | |
| 88 | var fn = hbs.compile('{{chop "foo bar baz _- "}}'); | ||
| 89 | 1 | expect(fn()).to.equal('foo bar baz'); | |
| 90 | 1 | done(); | |
| 91 | }); | ||
| 92 | }); | ||
| 93 | |||
| 94 | 1 | describe('dashcase', function() { | |
| 95 | it('should return an empty string if undefined', function(done) { | ||
| 96 | var fn = hbs.compile('{{dashcase}}'); | ||
| 97 | 1 | expect(fn()).to.equal(''); | |
| 98 | 1 | done(); | |
| 99 | }); | ||
| 100 | 1 | it('should return the string in dashcase', function(done) { | |
| 101 | var fn = hbs.compile('{{dashcase "foo bar baz qux"}}'); | ||
| 102 | 1 | expect(fn()).to.equal('foo-bar-baz-qux'); | |
| 103 | 1 | done(); | |
| 104 | }); | ||
| 105 | 1 | it('should lowercase a single character', function(done) { | |
| 106 | expect(hbs.compile('{{dashcase "f"}}')()).to.equal('f'); | ||
| 107 | 1 | expect(hbs.compile('{{dashcase "A"}}')()).to.equal('a'); | |
| 108 | 1 | done(); | |
| 109 | }); | ||
| 110 | }); | ||
| 111 | |||
| 112 | 1 | describe('dotcase', function() { | |
| 113 | it('should return an empty string if undefined', function(done) { | ||
| 114 | var fn = hbs.compile('{{dotcase}}'); | ||
| 115 | 1 | expect(fn()).to.equal(''); | |
| 116 | 1 | done(); | |
| 117 | }); | ||
| 118 | 1 | it('should return the string in dotcase', function(done) { | |
| 119 | var fn = hbs.compile('{{dotcase "foo bar baz qux"}}'); | ||
| 120 | 1 | expect(fn()).to.equal('foo.bar.baz.qux'); | |
| 121 | 1 | done(); | |
| 122 | }); | ||
| 123 | 1 | it('should lowercase a single character', function(done) { | |
| 124 | expect(hbs.compile('{{dotcase "f"}}')()).to.equal('f'); | ||
| 125 | 1 | expect(hbs.compile('{{dotcase "A"}}')()).to.equal('a'); | |
| 126 | 1 | done(); | |
| 127 | }); | ||
| 128 | }); | ||
| 129 | |||
| 130 | 1 | describe('hyphenate', function() { | |
| 131 | it('should return an empty string if undefined', function(done) { | ||
| 132 | var fn = hbs.compile('{{hyphenate}}'); | ||
| 133 | 1 | expect(fn()).to.equal(''); | |
| 134 | 1 | done(); | |
| 135 | }); | ||
| 136 | 1 | it('should return the string with spaces replaced with hyphens.', function(done) { | |
| 137 | var fn = hbs.compile('{{hyphenate "Bender should not be allowed on tv."}}'); | ||
| 138 | 1 | expect(fn()).to.equal('Bender-should-not-be-allowed-on-tv.'); | |
| 139 | 1 | done(); | |
| 140 | }); | ||
| 141 | }); | ||
| 142 | |||
| 143 | 1 | describe('lowercase', function() { | |
| 144 | it('should return an empty string if undefined', function(done) { | ||
| 145 | var fn = hbs.compile('{{lowercase}}'); | ||
| 146 | 1 | expect(fn()).to.equal(''); | |
| 147 | 1 | done(); | |
| 148 | }); | ||
| 149 | 1 | it('should return the string in lowercase', function(done) { | |
| 150 | var fn = hbs.compile('{{lowercase "BENDER SHOULD NOT BE ALLOWED ON TV"}}'); | ||
| 151 | 1 | expect(fn()).to.equal('bender should not be allowed on tv'); | |
| 152 | 1 | done(); | |
| 153 | }); | ||
| 154 | }); | ||
| 155 | |||
| 156 | 1 | describe('pascalcase', function() { | |
| 157 | it('should return an empty string if undefined', function(done) { | ||
| 158 | var fn = hbs.compile('{{pascalcase}}'); | ||
| 159 | 1 | expect(fn()).to.equal(''); | |
| 160 | 1 | done(); | |
| 161 | }); | ||
| 162 | 1 | it('should return the string in pascalcase', function(done) { | |
| 163 | var fn = hbs.compile('{{pascalcase "foo bar baz qux"}}'); | ||
| 164 | 1 | expect(fn()).to.equal('FooBarBazQux'); | |
| 165 | 1 | done(); | |
| 166 | }); | ||
| 167 | 1 | it('should uppercase a single character', function(done) { | |
| 168 | expect(hbs.compile('{{pascalcase "f"}}')()).to.equal('F'); | ||
| 169 | 1 | expect(hbs.compile('{{pascalcase "A"}}')()).to.equal('A'); | |
| 170 | 1 | done(); | |
| 171 | }); | ||
| 172 | }); | ||
| 173 | |||
| 174 | 1 | describe('pathcase', function() { | |
| 175 | it('should return an empty string if undefined', function(done) { | ||
| 176 | var fn = hbs.compile('{{pathcase}}'); | ||
| 177 | 1 | expect(fn()).to.equal(''); | |
| 178 | 1 | done(); | |
| 179 | }); | ||
| 180 | 1 | it('should return the string in pathcase', function(done) { | |
| 181 | var fn = hbs.compile('{{pathcase "foo bar baz qux"}}'); | ||
| 182 | 1 | expect(fn()).to.equal('foo/bar/baz/qux'); | |
| 183 | 1 | done(); | |
| 184 | }); | ||
| 185 | 1 | it('should lowercase a single character', function(done) { | |
| 186 | expect(hbs.compile('{{pathcase "f"}}')()).to.equal('f'); | ||
| 187 | 1 | expect(hbs.compile('{{pathcase "A"}}')()).to.equal('a'); | |
| 188 | 1 | done(); | |
| 189 | }); | ||
| 190 | }); | ||
| 191 | |||
| 192 | 1 | describe('plusify', function() { | |
| 193 | it('should return an empty string if undefined', function(done) { | ||
| 194 | var fn = hbs.compile('{{plusify}}'); | ||
| 195 | 1 | expect(fn()).to.equal(''); | |
| 196 | 1 | done(); | |
| 197 | }); | ||
| 198 | 1 | it('should return the empty string with no change.', function(done) { | |
| 199 | var fn = hbs.compile('{{plusify ""}}'); | ||
| 200 | 1 | expect(fn()).to.equal(''); | |
| 201 | 1 | done(); | |
| 202 | }); | ||
| 203 | 1 | it('should return the string with no change.', function(done) { | |
| 204 | var fn = hbs.compile('{{plusify "BenderShouldNotBeAllowedOnTv."}}'); | ||
| 205 | 1 | expect(fn()).to.equal('BenderShouldNotBeAllowedOnTv.'); | |
| 206 | 1 | done(); | |
| 207 | }); | ||
| 208 | 1 | it('should return the string with spaces replaced with pluses.', function(done) { | |
| 209 | var fn = hbs.compile('{{plusify "Bender should not be allowed on tv."}}'); | ||
| 210 | 1 | expect(fn()).to.equal('Bender+should+not+be+allowed+on+tv.'); | |
| 211 | 1 | done(); | |
| 212 | }); | ||
| 213 | }); | ||
| 214 | |||
| 215 | 1 | describe('replace', function() { | |
| 216 | it('should return an empty string if undefined', function(done) { | ||
| 217 | var fn = hbs.compile('{{replace}}'); | ||
| 218 | 1 | expect(fn()).to.equal(''); | |
| 219 | 1 | done(); | |
| 220 | }); | ||
| 221 | 1 | it('should replace occurrences of string "A" with string "B"', function(done) { | |
| 222 | var fn = hbs.compile('{{replace "Bender Bending Rodriguez" "B" "M"}}'); | ||
| 223 | 1 | expect(fn()).to.equal('Mender Mending Rodriguez'); | |
| 224 | 1 | done(); | |
| 225 | }); | ||
| 226 | 1 | it('should return the string if `a` is undefined', function(done) { | |
| 227 | var fn = hbs.compile('{{replace "a b c"}}'); | ||
| 228 | 1 | expect(fn()).to.equal('a b c'); | |
| 229 | 1 | done(); | |
| 230 | }); | ||
| 231 | 1 | it('should replace the string with `""` if `b` is undefined', function(done) { | |
| 232 | var fn = hbs.compile('{{replace "a b c" "a"}}'); | ||
| 233 | 1 | expect(fn()).to.equal(' b c'); | |
| 234 | 1 | done(); | |
| 235 | }); | ||
| 236 | }); | ||
| 237 | |||
| 238 | 1 | describe('reverse', function() { | |
| 239 | it('should return an empty string if undefined', function(done) { | ||
| 240 | var fn = hbs.compile('{{reverse}}'); | ||
| 241 | 1 | expect(fn()).to.equal(''); | |
| 242 | 1 | done(); | |
| 243 | }); | ||
| 244 | 1 | it('should return the string in reverse.', function(done) { | |
| 245 | var fn = hbs.compile('{{reverse "bender should NOT be allowed on TV."}}'); | ||
| 246 | 1 | expect(fn()).to.equal('.VT no dewolla eb TON dluohs redneb'); | |
| 247 | 1 | done(); | |
| 248 | }); | ||
| 249 | }); | ||
| 250 | |||
| 251 | 1 | describe('sentence', function() { | |
| 252 | it('should return an empty string if undefined', function(done) { | ||
| 253 | var fn = hbs.compile('{{sentence}}'); | ||
| 254 | 1 | expect(fn()).to.equal(''); | |
| 255 | 1 | done(); | |
| 256 | }); | ||
| 257 | 1 | it('should capitalize the first word of each sentence in a string and convert the rest of the sentence to lowercase.', function(done) { | |
| 258 | var fn = hbs.compile('{{sentence "bender should NOT be allowed on TV. fry SHOULD be allowed on TV."}}'); | ||
| 259 | 1 | expect(fn()).to.equal('Bender should not be allowed on tv. Fry should be allowed on tv.'); | |
| 260 | 1 | done(); | |
| 261 | }); | ||
| 262 | }); | ||
| 263 | |||
| 264 | 1 | describe('snakecase', function() { | |
| 265 | it('should return an empty string if undefined', function(done) { | ||
| 266 | var fn = hbs.compile('{{snakecase}}'); | ||
| 267 | 1 | expect(fn()).to.equal(''); | |
| 268 | 1 | done(); | |
| 269 | }); | ||
| 270 | 1 | it('should lowercase a single character', function(done) { | |
| 271 | expect(hbs.compile('{{snakecase "a"}}')()).to.equal('a'); | ||
| 272 | 1 | expect(hbs.compile('{{snakecase "A"}}')()).to.equal('a'); | |
| 273 | 1 | done(); | |
| 274 | }); | ||
| 275 | 1 | it('should return the string in snakecase', function(done) { | |
| 276 | var fn = hbs.compile('{{snakecase "foo bar baz qux"}}'); | ||
| 277 | 1 | expect(fn()).to.equal('foo_bar_baz_qux'); | |
| 278 | 1 | done(); | |
| 279 | }); | ||
| 280 | }); | ||
| 281 | |||
| 282 | 1 | describe('split', function() { | |
| 283 | it('should return an empty string if undefined', function(done) { | ||
| 284 | var fn = hbs.compile('{{split}}'); | ||
| 285 | 1 | expect(fn()).to.equal(''); | |
| 286 | 1 | done(); | |
| 287 | }); | ||
| 288 | 1 | it('should split the string with the default character', function(done) { | |
| 289 | var fn = hbs.compile('{{#each (split "a,b,c")}}<{{.}}>{{/each}}'); | ||
| 290 | 1 | expect(fn()).to.equal('<a><b><c>'); | |
| 291 | 1 | done(); | |
| 292 | }); | ||
| 293 | 1 | it('should split the string on the given character', function(done) { | |
| 294 | var fn = hbs.compile('{{#each (split "a|b|c" "|")}}<{{.}}>{{/each}}'); | ||
| 295 | 1 | expect(fn()).to.equal('<a><b><c>'); | |
| 296 | 1 | done(); | |
| 297 | }); | ||
| 298 | }); | ||
| 299 | |||
| 300 | 1 | describe('titleize', function() { | |
| 301 | it('should return an empty string if undefined', function(done) { | ||
| 302 | var fn = hbs.compile('{{titleize}}'); | ||
| 303 | 1 | expect(fn()).to.equal(''); | |
| 304 | 1 | done(); | |
| 305 | }); | ||
| 306 | |||
| 307 | 1 | it('should return unchanged input string if string has only non-word characters', function(done) { | |
| 308 | var fn = hbs.compile('{{titleize ",!"}}'); | ||
| 309 | 1 | expect(fn()).to.equal(',!'); | |
| 310 | 1 | done(); | |
| 311 | }); | ||
| 312 | |||
| 313 | 1 | it('should return the string in title case.', function(done) { | |
| 314 | var fn = hbs.compile('{{titleize "Bender-should-Not-be-allowed_on_Tv"}}'); | ||
| 315 | 1 | expect(fn()).to.equal('Bender Should Not Be Allowed On Tv'); | |
| 316 | 1 | done(); | |
| 317 | }); | ||
| 318 | }); | ||
| 319 | |||
| 320 | 1 | describe('trim', function() { | |
| 321 | it('should return an empty string if undefined', function(done) { | ||
| 322 | var fn = hbs.compile('{{trim}}'); | ||
| 323 | 1 | expect(fn()).to.equal(''); | |
| 324 | 1 | done(); | |
| 325 | }); | ||
| 326 | 1 | it('should trim leading whitespace', function(done) { | |
| 327 | var fn = hbs.compile('{{trim " foo"}}'); | ||
| 328 | 1 | expect(fn()).to.equal('foo'); | |
| 329 | 1 | done(); | |
| 330 | }); | ||
| 331 | 1 | it('should trim trailing whitespace', function(done) { | |
| 332 | var fn = hbs.compile('{{trim "foo "}}'); | ||
| 333 | 1 | expect(fn()).to.equal('foo'); | |
| 334 | 1 | done(); | |
| 335 | }); | ||
| 336 | }); | ||
| 337 | |||
| 338 | 1 | describe('startsWith', function() { | |
| 339 | it('should return an empty string if undefined', function(done) { | ||
| 340 | var fn = hbs.compile('{{startsWith}}'); | ||
| 341 | 1 | expect(fn()).to.equal(''); | |
| 342 | 1 | done(); | |
| 343 | }); | ||
| 344 | 1 | it('should render "Yes he is", from inside the block.', function(done) { | |
| 345 | var fn = hbs.compile('{{#startsWith "Bender" "Bender is great"}}Yes he is{{/startsWith}}'); | ||
| 346 | 1 | expect(fn()).to.equal("Yes he is"); | |
| 347 | 1 | done(); | |
| 348 | }); | ||
| 349 | 1 | it('should render the Inverse block.', function(done) { | |
| 350 | var fn = hbs.compile('{{#startsWith "Goodbye" "Hello, world!"}}Whoops{{else}}Bro, do you even hello world?{{/startsWith}}'); | ||
| 351 | 1 | expect(fn()).to.equal('Bro, do you even hello world?'); | |
| 352 | 1 | done(); | |
| 353 | }); | ||
| 354 | 1 | it("should render the Inverse block.", function(done) { | |
| 355 | var fn = hbs.compile('{{#startsWith "myPrefix" nullProperty}}fn block{{else}}inverse block{{/startsWith}}'); | ||
| 356 | 1 | expect(fn()).to.equal('inverse block'); | |
| 357 | 1 | done(); | |
| 358 | }); | ||
| 359 | }); | ||
| 360 | |||
| 361 | 1 | describe('uppercase', function() { | |
| 362 | it('should return an empty string if undefined', function(done) { | ||
| 363 | var fn = hbs.compile('{{uppercase}}'); | ||
| 364 | 1 | expect(fn()).to.equal(''); | |
| 365 | 1 | done(); | |
| 366 | }); | ||
| 367 | |||
| 368 | 1 | it('should return the string in uppercase', function(done) { | |
| 369 | var fn = hbs.compile('{{uppercase "bender should not be allowed on tv"}}'); | ||
| 370 | 1 | expect(fn()).to.equal('BENDER SHOULD NOT BE ALLOWED ON TV'); | |
| 371 | 1 | done(); | |
| 372 | }); | ||
| 373 | |||
| 374 | 1 | it('should work as a block helper', function(done) { | |
| 375 | var fn = hbs.compile('{{#uppercase}}bender should not be allowed on tv{{/uppercase}}'); | ||
| 376 | 1 | expect(fn()).to.equal('BENDER SHOULD NOT BE ALLOWED ON TV'); | |
| 377 | 1 | done(); | |
| 378 | }); | ||
| 379 | }); | ||
| 380 | }); | ||
| 381 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe | |
| 7 | |||
| 8 | 1 | const { buildRenderer } = require('../../spec-helpers'); | |
| 9 | 1 | const renderer = buildRenderer(); | |
| 10 | 1 | const hbs = renderer.handlebars; | |
| 11 | 1 | const helpers = require('../../../helpers/3p/misc'); | |
| 12 | |||
| 13 | 1 | Object.keys(helpers).forEach(key => { | |
| 14 | hbs.registerHelper(key, helpers[key]); | ||
| 15 | }); | ||
| 16 | |||
| 17 | |||
| 18 | 1 | describe('url', function() { | |
| 19 | describe('urlResolve', function() { | ||
| 20 | it('should take a base URL, and a href URL, and resolve them as a browser would', function(done) { | ||
| 21 | var fn = hbs.compile('{{urlResolve "/one/two/three" "four"}}'); | ||
| 22 | 1 | expect(fn()).to.equal('/one/two/four'); | |
| 23 | 1 | done(); | |
| 24 | }); | ||
| 25 | |||
| 26 | 1 | it('should take a base URL, and a href URL, and resolve them as a browser would', function(done) { | |
| 27 | var fn = hbs.compile('{{urlResolve "http://example.com/" "/one"}}'); | ||
| 28 | 1 | expect(fn()).to.equal('http://example.com/one'); | |
| 29 | 1 | done(); | |
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should take a base URL, and a href URL, and resolve them as a browser would', function(done) { | |
| 33 | var fn = hbs.compile('{{urlResolve "http://example.com/one" "/two"}}'); | ||
| 34 | 1 | expect(fn()).to.equal('http://example.com/two'); | |
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | }); | ||
| 38 | |||
| 39 | 1 | describe('encodeURI', function() { | |
| 40 | it('should return an encoded uri string.', function(done) { | ||
| 41 | var fn = hbs.compile('{{encodeURI "http://example.com?comment=Thyme &time=again"}}'); | ||
| 42 | 1 | expect(fn()).to.equal('http%3A%2F%2Fexample.com%3Fcomment%3DThyme%20%26time%3Dagain'); | |
| 43 | 1 | done(); | |
| 44 | }); | ||
| 45 | }); | ||
| 46 | |||
| 47 | 1 | describe('decodeURI', function() { | |
| 48 | it('should return an decoded uri string.', function(done) { | ||
| 49 | var fn = hbs.compile('{{{decodeURI "http%3A%2F%2Fexample.com%3Fcomment%3DThyme%20%26time%3Dagain"}}}'); | ||
| 50 | 1 | expect(fn()).to.equal('http://example.com?comment=Thyme &time=again'); | |
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | describe('urlParse', function() { | |
| 56 | it('should take a string, and return an object stringified to JSON.', function(done) { | ||
| 57 | var fn = hbs.compile('{{{JSONstringify (urlParse "http://foo.com/bar/baz?key=value" "json")}}}'); | ||
| 58 | 1 | expect(JSON.parse(fn())).to.equal({ | |
| 59 | "protocol": "http:", | ||
| 60 | "slashes": true, | ||
| 61 | "auth": null, | ||
| 62 | "host": "foo.com", | ||
| 63 | "port": null, | ||
| 64 | "hostname": "foo.com", | ||
| 65 | "hash": null, | ||
| 66 | "search": "?key=value", | ||
| 67 | "query": "key=value", | ||
| 68 | "pathname": "/bar/baz", | ||
| 69 | "path": "/bar/baz?key=value", | ||
| 70 | "href": "http://foo.com/bar/baz?key=value" | ||
| 71 | }); | ||
| 72 | 1 | done(); | |
| 73 | }); | ||
| 74 | }); | ||
| 75 | |||
| 76 | 1 | describe('strip protocol', function() { | |
| 77 | it('should take an http url and return without the protocol', function(done) { | ||
| 78 | var data = { testUrl: 'http://foo.bar' }; | ||
| 79 | 1 | var expectedResult = '//foo.bar/'; | |
| 80 | 1 | var fn = hbs.compile('{{stripProtocol testUrl}}'); | |
| 81 | 1 | expect(fn(data)).to.equal(expectedResult); | |
| 82 | 1 | done(); | |
| 83 | }); | ||
| 84 | |||
| 85 | 1 | it('strip https protocol', function(done) { | |
| 86 | var data = { testUrl: 'https://foo.bar' }; | ||
| 87 | 1 | var expectedResult = '//foo.bar/'; | |
| 88 | 1 | var fn = hbs.compile('{{stripProtocol testUrl}}'); | |
| 89 | 1 | expect(fn(data)).to.equal(expectedResult); | |
| 90 | 1 | done(); | |
| 91 | }); | ||
| 92 | |||
| 93 | 1 | it('should leave a relative url unchanged', function(done) { | |
| 94 | var testUrl = 'path/to/file'; | ||
| 95 | 1 | var data = { testUrl: testUrl }; | |
| 96 | 1 | var fn = hbs.compile('{{stripProtocol testUrl}}'); | |
| 97 | 1 | expect(fn(data)).to.equal(testUrl); | |
| 98 | 1 | done(); | |
| 99 | }); | ||
| 100 | |||
| 101 | 1 | it('should leave an absolute url unchanged', function(done) { | |
| 102 | var testUrl = '/path/to/file'; | ||
| 103 | 1 | var data = { testUrl: testUrl }; | |
| 104 | 1 | var fn = hbs.compile('{{stripProtocol testUrl}}'); | |
| 105 | 1 | expect(fn(data)).to.equal(testUrl); | |
| 106 | 1 | done(); | |
| 107 | }); | ||
| 108 | |||
| 109 | }); | ||
| 110 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COPY of https://raw.githubusercontent.com/jonschlinkert/arr-filter/1.1.1/test.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 1 | const Code = require('code'); | |
| 6 | 1 | const Lab = require('lab'); | |
| 7 | 1 | const lab = exports.lab = Lab.script(); | |
| 8 | 1 | const expect = Code.expect; | |
| 9 | 1 | const it = lab.it; | |
| 10 | 1 | const describe = lab.describe; | |
| 11 | 1 | const path = require('path'); | |
| 12 | |||
| 13 | 1 | const filter = require('../../../../../helpers/3p/utils/lib/arrayFilter'); | |
| 14 | |||
| 15 | 1 | describe('filter:', function () { | |
| 16 | it('should filter strings from the array:', function (done) { | ||
| 17 | var actual = filter(['a', {a: 'b'}, 1, 'b', 2, {c: 'd'}, 'c'], function (ele) { | ||
| 18 | return typeof ele === 'string'; | ||
| 19 | }); | ||
| 20 | 1 | expect(actual).to.equal(['a', 'b', 'c']); | |
| 21 | 1 | done(); | |
| 22 | }); | ||
| 23 | |||
| 24 | 1 | it('should filter objects:', function (done) { | |
| 25 | var actual = filter(['a', {a: 'b'}, 1, 'b', 2, {c: 'd'}, 'c'], function (ele) { | ||
| 26 | return typeof ele === 'object'; | ||
| 27 | }); | ||
| 28 | 1 | expect(actual).to.equal([{a: 'b'}, {c: 'd'}]); | |
| 29 | 1 | done(); | |
| 30 | }); | ||
| 31 | |||
| 32 | 1 | it('should filter strings:', function (done) { | |
| 33 | function ext(extension) { | ||
| 34 | 2 | return function(fp) { | |
| 35 | return path.extname(fp) === extension; | ||
| 36 | }; | ||
| 37 | } | ||
| 38 | |||
| 39 | 1 | expect(filter(['a/b/c.js', 'a/b/c.css'], ext('.css'))).to.equal(['a/b/c.css']); | |
| 40 | 1 | expect(filter(['a/b/c.js', 'a/b/c.css'], ext('.js'))).to.equal(['a/b/c.js']); | |
| 41 | 1 | done(); | |
| 42 | }); | ||
| 43 | |||
| 44 | 1 | it('should throw an error when the callback is missing.', function (done) { | |
| 45 | expect(function() { | ||
| 46 | filter(['a/b/c.js', 'a/b/c.css']); | ||
| 47 | }).to.throw('arr-filter expects a callback function.'); | ||
| 48 | 1 | done(); | |
| 49 | }); | ||
| 50 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const flatten = require('../../../../../helpers/3p/utils/lib/arrayFlatten'); | |
| 9 | |||
| 10 | 1 | describe('flatten', function() { | |
| 11 | it('should flatten nested arrays:', function(done) { | ||
| 12 | expect(flatten(['a', 'b', ['c'], 'd', ['e']])).to.equal(['a', 'b', 'c', 'd', 'e']); | ||
| 13 | 1 | done(); | |
| 14 | }); | ||
| 15 | |||
| 16 | 1 | it('should flatten deeply nested arrays:', function(done) { | |
| 17 | expect(flatten(['a', [[[[[[[['b', [['c']]]]]], 'd', ['e']]]]]])).to.equal(['a', 'b', 'c', 'd', 'e']); | ||
| 18 | 1 | expect(flatten(['a', 'b', ['c'], 'd', ['e']])).to.equal(['a', 'b', 'c', 'd', 'e']); | |
| 19 | 1 | expect(flatten([['a', ['b', ['k', ['a', ['b', ['c'], [['a', [['a', ['b', ['k', ['a', ['b', ['c']], ['a', ['x', ['c'], ['a', ['x', ['k']]], ['d', ['z']]]], ['d', ['m']]], ['d', ['e']]]]], ['d', ['e']]], ['b', ['k', ['a', ['b', ['c']], ['a', ['x', ['c'], ['a', ['x', ['k']]], ['d', ['z']]]], ['d', ['m']]], ['d', ['e']]]]], ['d', ['e']]]], ['a', ['x', ['c'], ['a', ['x', ['k']], [['a', ['b', ['k', ['a', ['b', ['c']], ['a', ['x', ['c'], ['a', ['x', ['k']]], ['d', ['z']]]], ['d', ['m']]], ['d', ['e']]]]], ['d', ['e']]]], ['d', ['z']]]], ['d', ['m']]], ['d', ['e']]]]], ['d', ['e']]])).to.equal([ 'a', 'b', 'k', 'a', 'b', 'c', 'a', 'a', 'b', 'k', 'a', 'b', 'c', 'a', 'x', 'c', 'a', 'x', 'k', 'd', 'z', 'd', 'm', 'd', 'e', 'd', 'e', 'b', 'k', 'a', 'b', 'c', 'a', 'x', 'c', 'a', 'x', 'k', 'd', 'z', 'd', 'm', 'd', 'e', 'd', 'e', 'a', 'x', 'c', 'a', 'x', 'k', 'a', 'b', 'k', 'a', 'b', 'c', 'a', 'x', 'c', 'a', 'x', 'k', 'd', 'z', 'd', 'm', 'd', 'e', 'd', 'e', 'd', 'z', 'd', 'm', 'd', 'e', 'd', 'e' ]); | |
| 20 | 1 | done(); | |
| 21 | }); | ||
| 22 | }); | ||
| 23 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const arraySort = require('../../../../../helpers/3p/utils/lib/arraySort'); | |
| 9 | 1 | const {getValue: get } = require('../../../../../helpers/lib/common'); | |
| 10 | |||
| 11 | 1 | describe('errors', function() { | |
| 12 | it('should throw an error when invalid args are passed:', function(done) { | ||
| 13 | expect(function() { | ||
| 14 | arraySort({}); | ||
| 15 | }).to.throw('array-sort expects an array.'); | ||
| 16 | 1 | done(); | |
| 17 | }); | ||
| 18 | }); | ||
| 19 | |||
| 20 | 1 | describe('empty array', function() { | |
| 21 | it('should return an empty array when null or undefined is passed', function(done) { | ||
| 22 | expect(arraySort()).to.equal([]); | ||
| 23 | 1 | expect(arraySort(undefined)).to.equal([]); | |
| 24 | 1 | expect(arraySort(null)).to.equal([]); | |
| 25 | 1 | done(); | |
| 26 | }) | ||
| 27 | }); | ||
| 28 | |||
| 29 | 1 | describe('basic sort', function() { | |
| 30 | it('should sort an array of primitives', function(done) { | ||
| 31 | var arr = ['d', 3, 'b', 'a', 'd', 1, 0, 'z']; | ||
| 32 | 1 | expect(arraySort(arr)).to.equal([ 0, 1, 3, 'a', 'b', 'd', 'd', 'z' ]); | |
| 33 | 1 | done(); | |
| 34 | }) | ||
| 35 | }); | ||
| 36 | |||
| 37 | 1 | describe('arraySort', function() { | |
| 38 | var posts = [ | ||
| 39 | { path: 'a.md', locals: { date: '2014-01-09' } }, | ||
| 40 | { path: 'f.md', locals: { date: '2014-01-02' } }, | ||
| 41 | { path: 'd.md', locals: { date: '2013-05-06' } }, | ||
| 42 | { path: 'e.md', locals: { date: '2015-01-02' } }, | ||
| 43 | { path: 'b.md', locals: { date: '2012-01-02' } }, | ||
| 44 | { path: 'f.md', locals: { date: '2014-06-01' } }, | ||
| 45 | { path: 'c.md', locals: { date: '2015-04-12' } }, | ||
| 46 | { path: 'g.md', locals: { date: '2014-02-02' } }, | ||
| 47 | ]; | ||
| 48 | |||
| 49 | 1 | it('should sort by a property:', function(done) { | |
| 50 | var arr = [{key: 'y'}, {key: 'z'}, {key: 'x'}]; | ||
| 51 | 1 | expect(arraySort(arr, 'key')).to.equal([ | |
| 52 | {key: 'x'}, | ||
| 53 | {key: 'y'}, | ||
| 54 | {key: 'z'} | ||
| 55 | ]); | ||
| 56 | |||
| 57 | 1 | expect(arraySort(posts, 'path')).to.equal([ | |
| 58 | { path: 'a.md', locals: { date: '2014-01-09' } }, | ||
| 59 | { path: 'b.md', locals: { date: '2012-01-02' } }, | ||
| 60 | { path: 'c.md', locals: { date: '2015-04-12' } }, | ||
| 61 | { path: 'd.md', locals: { date: '2013-05-06' } }, | ||
| 62 | { path: 'e.md', locals: { date: '2015-01-02' } }, | ||
| 63 | { path: 'f.md', locals: { date: '2014-01-02' } }, | ||
| 64 | { path: 'f.md', locals: { date: '2014-06-01' } }, | ||
| 65 | { path: 'g.md', locals: { date: '2014-02-02' } } | ||
| 66 | ]); | ||
| 67 | 1 | done(); | |
| 68 | }); | ||
| 69 | |||
| 70 | 1 | it('should sort by a nested property:', function(done) { | |
| 71 | var res = expect(arraySort(posts, 'locals.date')); | ||
| 72 | 1 | res.to.equal([ | |
| 73 | { path: 'b.md', locals: { date: '2012-01-02' } }, | ||
| 74 | { path: 'd.md', locals: { date: '2013-05-06' } }, | ||
| 75 | { path: 'f.md', locals: { date: '2014-01-02' } }, | ||
| 76 | { path: 'a.md', locals: { date: '2014-01-09' } }, | ||
| 77 | { path: 'g.md', locals: { date: '2014-02-02' } }, | ||
| 78 | { path: 'f.md', locals: { date: '2014-06-01' } }, | ||
| 79 | { path: 'e.md', locals: { date: '2015-01-02' } }, | ||
| 80 | { path: 'c.md', locals: { date: '2015-04-12' } } | ||
| 81 | ]); | ||
| 82 | 1 | done(); | |
| 83 | }); | ||
| 84 | |||
| 85 | 1 | it('should do nothing when the specified property is not a string:', function(done) { | |
| 86 | var arr = [ | ||
| 87 | {a: {b: {c: 'c'}}}, | ||
| 88 | {a: {b: {z: 'z'}}}, | ||
| 89 | {a: {b: {u: 'u'}}}, | ||
| 90 | {a: {b: {y: 'y'}}} | ||
| 91 | ]; | ||
| 92 | |||
| 93 | 1 | expect(arraySort(arr, 'a.b')).to.equal([ | |
| 94 | {a: {b: {c: 'c'}}}, | ||
| 95 | {a: {b: {z: 'z'}}}, | ||
| 96 | {a: {b: {u: 'u'}}}, | ||
| 97 | {a: {b: {y: 'y'}}} | ||
| 98 | ]); | ||
| 99 | 1 | done(); | |
| 100 | }); | ||
| 101 | |||
| 102 | 1 | it('should sort by multiple properties:', function(done) { | |
| 103 | var posts = [ | ||
| 104 | { foo: 'bbb', locals: { date: '2013-05-06' } }, | ||
| 105 | { foo: 'aaa', locals: { date: '2012-01-02' } }, | ||
| 106 | { foo: 'ddd', locals: { date: '2015-04-12' } }, | ||
| 107 | { foo: 'ccc', locals: { date: '2014-01-02' } }, | ||
| 108 | { foo: 'ccc', locals: { date: '2015-01-02' } }, | ||
| 109 | { foo: 'ddd', locals: { date: '2014-01-09' } }, | ||
| 110 | { foo: 'bbb', locals: { date: '2014-06-01' } }, | ||
| 111 | { foo: 'aaa', locals: { date: '2014-02-02' } }, | ||
| 112 | ]; | ||
| 113 | |||
| 114 | 1 | var actual = arraySort(posts, ['foo', 'locals.date']); | |
| 115 | |||
| 116 | 1 | expect(actual).to.equal([ | |
| 117 | { foo: 'aaa', locals: { date: '2012-01-02' } }, | ||
| 118 | { foo: 'aaa', locals: { date: '2014-02-02' } }, | ||
| 119 | { foo: 'bbb', locals: { date: '2013-05-06' } }, | ||
| 120 | { foo: 'bbb', locals: { date: '2014-06-01' } }, | ||
| 121 | { foo: 'ccc', locals: { date: '2014-01-02' } }, | ||
| 122 | { foo: 'ccc', locals: { date: '2015-01-02' } }, | ||
| 123 | { foo: 'ddd', locals: { date: '2014-01-09' } }, | ||
| 124 | { foo: 'ddd', locals: { date: '2015-04-12' } } | ||
| 125 | ]); | ||
| 126 | 1 | done(); | |
| 127 | }); | ||
| 128 | |||
| 129 | // TODO: Not working! | ||
| 130 | |||
| 131 | // it('should sort with a function:', function(done) { | ||
| 132 | // var arr = [{key: 'y'}, {key: 'z'}, {key: 'x'}]; | ||
| 133 | |||
| 134 | // var actual = arraySort(arr, function(a, b) { | ||
| 135 | // return a.key > b.key; | ||
| 136 | // }); | ||
| 137 | |||
| 138 | // expect(actual).to.equal([ | ||
| 139 | // {key: 'x'}, | ||
| 140 | // {key: 'y'}, | ||
| 141 | // {key: 'z'} | ||
| 142 | // ]); | ||
| 143 | // done(); | ||
| 144 | // }); | ||
| 145 | |||
| 146 | 1 | it('should support sorting with a list of function:', function(done) { | |
| 147 | var arr = [ | ||
| 148 | {foo: 'w', bar: 'y', baz: 'w', quux: 'a'}, | ||
| 149 | {foo: 'x', bar: 'y', baz: 'w', quux: 'b'}, | ||
| 150 | {foo: 'x', bar: 'y', baz: 'z', quux: 'c'}, | ||
| 151 | {foo: 'x', bar: 'x', baz: 'w', quux: 'd'}, | ||
| 152 | ]; | ||
| 153 | |||
| 154 | 1 | var compare = function(prop) { | |
| 155 | return function(a, b) { | ||
| 156 | return a[prop].localeCompare(b[prop]); | ||
| 157 | }; | ||
| 158 | }; | ||
| 159 | |||
| 160 | 1 | var actual = arraySort(arr, | |
| 161 | compare('foo'), | ||
| 162 | compare('bar'), | ||
| 163 | compare('baz'), | ||
| 164 | compare('quux')); | ||
| 165 | |||
| 166 | 1 | expect(actual).to.equal([ | |
| 167 | { foo: 'w', bar: 'y', baz: 'w', quux: 'a' }, | ||
| 168 | { foo: 'x', bar: 'x', baz: 'w', quux: 'd' }, | ||
| 169 | { foo: 'x', bar: 'y', baz: 'w', quux: 'b' }, | ||
| 170 | { foo: 'x', bar: 'y', baz: 'z', quux: 'c' } | ||
| 171 | ]); | ||
| 172 | 1 | done(); | |
| 173 | }); | ||
| 174 | |||
| 175 | 1 | it('should support sorting with an array of function:', function(done) { | |
| 176 | var arr = [ | ||
| 177 | {foo: 'w', bar: 'y', baz: 'w', quux: 'a'}, | ||
| 178 | {foo: 'x', bar: 'y', baz: 'w', quux: 'b'}, | ||
| 179 | {foo: 'x', bar: 'y', baz: 'z', quux: 'c'}, | ||
| 180 | {foo: 'x', bar: 'x', baz: 'w', quux: 'd'}, | ||
| 181 | ]; | ||
| 182 | |||
| 183 | 1 | var compare = function(prop) { | |
| 184 | return function(a, b) { | ||
| 185 | return a[prop].localeCompare(b[prop]); | ||
| 186 | }; | ||
| 187 | }; | ||
| 188 | |||
| 189 | 1 | var actual = arraySort(arr, [ | |
| 190 | compare('foo'), | ||
| 191 | compare('bar'), | ||
| 192 | compare('baz'), | ||
| 193 | compare('quux') | ||
| 194 | ]); | ||
| 195 | |||
| 196 | 1 | expect(actual).to.equal([ | |
| 197 | { foo: 'w', bar: 'y', baz: 'w', quux: 'a' }, | ||
| 198 | { foo: 'x', bar: 'x', baz: 'w', quux: 'd' }, | ||
| 199 | { foo: 'x', bar: 'y', baz: 'w', quux: 'b' }, | ||
| 200 | { foo: 'x', bar: 'y', baz: 'z', quux: 'c' } | ||
| 201 | ]); | ||
| 202 | 1 | done(); | |
| 203 | }); | ||
| 204 | |||
| 205 | 1 | it('should support sorting with any combination of functions and properties:', function(done) { | |
| 206 | var posts = [ | ||
| 207 | { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, | ||
| 208 | { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, | ||
| 209 | { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, | ||
| 210 | { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, | ||
| 211 | { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, | ||
| 212 | { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, | ||
| 213 | { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, | ||
| 214 | { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, | ||
| 215 | { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, | ||
| 216 | { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, | ||
| 217 | { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, | ||
| 218 | { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, | ||
| 219 | { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, | ||
| 220 | ]; | ||
| 221 | |||
| 222 | 1 | var compare = function(prop) { | |
| 223 | return function(a, b, fn) { | ||
| 224 | var valA = get(a, prop); | ||
| 225 | 27 | var valB = get(b, prop); | |
| 226 | 27 | return fn(valA, valB); | |
| 227 | }; | ||
| 228 | }; | ||
| 229 | |||
| 230 | 1 | var actual = arraySort(posts, 'locals.date', 'doesnt.exist', compare('locals.foo'), [ | |
| 231 | compare('locals.bar') | ||
| 232 | ]); | ||
| 233 | |||
| 234 | 1 | expect(actual).to.equal([ | |
| 235 | { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, | ||
| 236 | { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, | ||
| 237 | { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, | ||
| 238 | { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, | ||
| 239 | { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, | ||
| 240 | { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, | ||
| 241 | { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, | ||
| 242 | { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, | ||
| 243 | { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, | ||
| 244 | { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, | ||
| 245 | { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, | ||
| 246 | { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, | ||
| 247 | { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } } | ||
| 248 | ]); | ||
| 249 | 1 | done(); | |
| 250 | }); | ||
| 251 | |||
| 252 | 1 | it('should support reverse sorting with any combination of functions and properties:', function(done) { | |
| 253 | var posts = [ | ||
| 254 | { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, | ||
| 255 | { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, | ||
| 256 | { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, | ||
| 257 | { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, | ||
| 258 | { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, | ||
| 259 | { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, | ||
| 260 | { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, | ||
| 261 | { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, | ||
| 262 | { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, | ||
| 263 | { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } }, | ||
| 264 | { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, | ||
| 265 | { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, | ||
| 266 | { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, | ||
| 267 | ]; | ||
| 268 | |||
| 269 | 1 | var compare = function(prop) { | |
| 270 | return function(a, b, fn) { | ||
| 271 | var valA = get(a, prop); | ||
| 272 | 24 | var valB = get(b, prop); | |
| 273 | 24 | return fn(valA, valB); | |
| 274 | }; | ||
| 275 | }; | ||
| 276 | |||
| 277 | 1 | var actual = arraySort(posts, 'locals.date', 'doesnt.exist', compare('locals.foo'), [ | |
| 278 | compare('locals.bar') | ||
| 279 | ], { reverse: true }); | ||
| 280 | |||
| 281 | 1 | expect(actual).to.equal([ | |
| 282 | { path: 'c.md', locals: { date: '2015-04-12', foo: 'ttt', bar: 11 } }, | ||
| 283 | { path: 'e.md', locals: { date: '2015-01-02', foo: 'aaa', bar: 8 } }, | ||
| 284 | { path: 'f.md', locals: { date: '2014-06-01', foo: 'rrr', bar: 10 } }, | ||
| 285 | { path: 'g.md', locals: { date: '2014-02-02', foo: 'yyy', bar: 12 } }, | ||
| 286 | { path: 'a.md', locals: { date: '2014-01-01', foo: 'zzz', bar: 1 } }, | ||
| 287 | { path: 'l.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 7 } }, | ||
| 288 | { path: 'h.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 6 } }, | ||
| 289 | { path: 'i.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 5 } }, | ||
| 290 | { path: 'j.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 4 } }, | ||
| 291 | { path: 'd.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 3 } }, | ||
| 292 | { path: 'k.md', locals: { date: '2014-01-01', foo: 'xxx', bar: 1 } }, | ||
| 293 | { path: 'f.md', locals: { date: '2014-01-01', foo: 'mmm', bar: 2 } }, | ||
| 294 | { path: 'b.md', locals: { date: '2012-01-02', foo: 'ccc', bar: 9 } } | ||
| 295 | ]); | ||
| 296 | 1 | done(); | |
| 297 | }); | ||
| 298 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const createFrame = require('../../../../../helpers/3p/utils/lib/createFrame'); | |
| 9 | |||
| 10 | 1 | var hbs = require('@bigcommerce/handlebars-v4'); | |
| 11 | |||
| 12 | 1 | describe('createFrame', function () { | |
| 13 | it('should create a reference to _parent:', function (done) { | ||
| 14 | var obj = createFrame({}); | ||
| 15 | 1 | expect(obj.hasOwnProperty('_parent')).to.equal(true); | |
| 16 | 1 | done(); | |
| 17 | }); | ||
| 18 | |||
| 19 | 1 | it('should expose a non-enumerable `extend` method:', function (done) { | |
| 20 | var obj = createFrame({}); | ||
| 21 | 1 | expect(typeof obj.extend).to.equal('function'); | |
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should extend the frame object with the `extend` method:', function (done) { | |
| 26 | var obj = createFrame({}); | ||
| 27 | 1 | obj.extend({a: 'aaa'}); | |
| 28 | 1 | obj.extend({b: 'bbb'}); | |
| 29 | 1 | expect(obj.hasOwnProperty('a')).to.equal(true); | |
| 30 | 1 | expect(obj.hasOwnProperty('b')).to.equal(true); | |
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('should extend the frame object with additional objects:', function (done) { | |
| 35 | var obj = createFrame({}, {a: 'aaa'}, {b: 'bbb'}); | ||
| 36 | 1 | expect(obj.hasOwnProperty('a')).to.equal(true); | |
| 37 | 1 | expect(obj.hasOwnProperty('b')).to.equal(true); | |
| 38 | 1 | done(); | |
| 39 | }); | ||
| 40 | |||
| 41 | 1 | it('should work with sparse arguments:', function (done) { | |
| 42 | var obj = createFrame({}, undefined, {b: 'bbb'}); | ||
| 43 | 1 | expect(obj.hasOwnProperty('b')).to.equal(true); | |
| 44 | 1 | done(); | |
| 45 | }); | ||
| 46 | |||
| 47 | 1 | it('should add private variables when passed to `options.fn()`:', function (done) { | |
| 48 | var tmpl = '{{#foo}}{{@a}}{{@b}}{{/foo}}'; | ||
| 49 | |||
| 50 | 1 | hbs.registerHelper('foo', function (context) { | |
| 51 | var frame = createFrame(context.data); | ||
| 52 | 1 | frame.extend({a: 'aaa'}); | |
| 53 | 1 | frame.extend({b: 'bbb'}); | |
| 54 | 1 | return context.fn(context, {data: frame}); | |
| 55 | }); | ||
| 56 | 1 | var fn = hbs.compile(tmpl); | |
| 57 | 1 | expect(fn({})).to.equal('aaabbb'); | |
| 58 | 1 | done(); | |
| 59 | }); | ||
| 60 | |||
| 61 | 1 | it('should create private variables from options hash properties', function (done) { | |
| 62 | var tmpl = '{{#foo options target=xxx}}{{@a}}{{@b}}{{log}}{{@target.yyy}}{{/foo}}'; | ||
| 63 | 1 | var context = { | |
| 64 | options: {data: {eee: 'fff'} }, | ||
| 65 | xxx: {yyy: 'zzz'} | ||
| 66 | }; | ||
| 67 | 1 | hbs.registerHelper('foo', function (context, options) { | |
| 68 | var frame = createFrame(context.data); | ||
| 69 | 1 | frame.extend({a: 'aaa'}); | |
| 70 | 1 | frame.extend({b: 'bbb'}); | |
| 71 | 1 | frame.extend(options.hash); | |
| 72 | 1 | return options.fn(context, {data: frame}); | |
| 73 | }); | ||
| 74 | |||
| 75 | 1 | var fn = hbs.compile(tmpl); | |
| 76 | 1 | expect(fn(context)).to.equal('aaabbbzzz'); | |
| 77 | 1 | done(); | |
| 78 | }); | ||
| 79 | |||
| 80 | 1 | it('should throw an error if args are invalid:', function (done) { | |
| 81 | expect(function () { | ||
| 82 | createFrame(); | ||
| 83 | }, /createFrame expects data to be an object/).to.throw(); | ||
| 84 | 1 | done(); | |
| 85 | }); | ||
| 86 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const forIn = require('../../../../../helpers/3p/utils/lib/forIn'); | |
| 9 | |||
| 10 | |||
| 11 | 1 | describe('.forIn()', function() { | |
| 12 | it('should loop through all properties in the object.', function(done) { | ||
| 13 | var obj = {a: 'foo', b: 'bar', c: 'baz'}; | ||
| 14 | 1 | var values = []; | |
| 15 | 1 | var keys = []; | |
| 16 | |||
| 17 | 1 | forIn(obj, function(value, key, o) { | |
| 18 | expect(o).to.equal(obj); | ||
| 19 | 3 | keys.push(key); | |
| 20 | 3 | values.push(value); | |
| 21 | }); | ||
| 22 | |||
| 23 | 1 | expect(keys).to.equal(['a', 'b', 'c']); | |
| 24 | 1 | expect(values).to.equal(['foo', 'bar', 'baz']); | |
| 25 | 1 | done(); | |
| 26 | }); | ||
| 27 | |||
| 28 | 1 | it('should break the loop early if `false` is returned.', function(done) { | |
| 29 | var obj = {a: 'foo', b: 'bar', c: 'baz'}; | ||
| 30 | 1 | var values = []; | |
| 31 | 1 | var keys = []; | |
| 32 | |||
| 33 | 1 | forIn(obj, function(value, key, o) { | |
| 34 | if (key === 'b') { | ||
| 35 | 1 | return false; | |
| 36 | } | ||
| 37 | 1 | keys.push(key); | |
| 38 | 1 | values.push(value); | |
| 39 | }); | ||
| 40 | |||
| 41 | 1 | expect(keys).to.equal(['a']); | |
| 42 | 1 | expect(values).to.equal(['foo']); | |
| 43 | 1 | done(); | |
| 44 | }); | ||
| 45 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * COPY of https://github.com/jonschlinkert/for-own/tree/0.1.5/index.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 1 | const Code = require('code'); | |
| 6 | 1 | const Lab = require('lab'); | |
| 7 | 1 | const lab = exports.lab = Lab.script(); | |
| 8 | 1 | const expect = Code.expect; | |
| 9 | 1 | const it = lab.it; | |
| 10 | 1 | const describe = lab.describe; | |
| 11 | |||
| 12 | 1 | const forOwn = require('../../../../../helpers/3p/utils/lib/forOwn'); | |
| 13 | |||
| 14 | |||
| 15 | 1 | describe('forOwn', function() { | |
| 16 | it('should expose keys and values from the given object', function(done) { | ||
| 17 | var obj = {a: 'foo', b: 'bar', c: 'baz'}; | ||
| 18 | 1 | var values = []; | |
| 19 | 1 | var keys = []; | |
| 20 | |||
| 21 | 1 | forOwn(obj, function(value, key, o) { | |
| 22 | expect(o).to.equal(obj); | ||
| 23 | 3 | keys.push(key); | |
| 24 | 3 | values.push(value); | |
| 25 | }); | ||
| 26 | |||
| 27 | 1 | expect(keys).to.equal(['a', 'b', 'c']); | |
| 28 | 1 | expect(values).to.equal(['foo', 'bar', 'baz']); | |
| 29 | 1 | done(); | |
| 30 | }); | ||
| 31 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /*! | ||
| 2 | * index-of <https://github.com/jonschlinkert/index-of> | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Jon Schlinkert, contributors. | ||
| 5 | * Licensed under the MIT License | ||
| 6 | */ | ||
| 7 | |||
| 8 | 1 | const Code = require('code'); | |
| 9 | 1 | const Lab = require('lab'); | |
| 10 | 1 | const lab = exports.lab = Lab.script(); | |
| 11 | 1 | const expect = Code.expect; | |
| 12 | 1 | const it = lab.it; | |
| 13 | 1 | const describe = lab.describe; | |
| 14 | |||
| 15 | 1 | const indexOf = require('../../../../../helpers/3p/utils/lib/indexOf'); | |
| 16 | |||
| 17 | 1 | describe('indexOf', function () { | |
| 18 | it('should get the index of the given element:', function (done) { | ||
| 19 | expect(indexOf(['a', 'b', 'c'], 'a')).to.equal(0); | ||
| 20 | 1 | expect(indexOf(['a', 'b', 'c'], 'b')).to.equal(1); | |
| 21 | 1 | expect(indexOf(['a', 'b', 'c'], 'c')).to.equal(2); | |
| 22 | 1 | done(); | |
| 23 | }); | ||
| 24 | |||
| 25 | 1 | it('should return -1 if fromIndex is out of range:', function (done) { | |
| 26 | expect(indexOf(['a', undefined, 'b', 'c', 'a'], undefined, 5)).to.equal(-1); | ||
| 27 | 1 | done(); | |
| 28 | }); | ||
| 29 | |||
| 30 | 1 | it('should return -1 if the element does not exist:', function (done) { | |
| 31 | expect(indexOf(['a', 'b', 'c'], 'd')).to.equal(-1); | ||
| 32 | 1 | done(); | |
| 33 | }); | ||
| 34 | |||
| 35 | 1 | it('should return -1 if the array is undefined', function (done) { | |
| 36 | expect(indexOf(undefined, 'd')).to.equal(-1); | ||
| 37 | 1 | done(); | |
| 38 | }); | ||
| 39 | |||
| 40 | 1 | it('should get the index, starting from the given index:', function (done) { | |
| 41 | expect(indexOf(['a', 'b', 'c', 'a', 'b', 'c'], 'b', 2)).to.equal(4); | ||
| 42 | 1 | expect(indexOf(['a', undefined, 'b', 'c', 'a'], undefined, 0)).to.equal(1); | |
| 43 | 1 | expect(indexOf(['a', undefined, 'b', 'c', 'a'], undefined, 1)).to.equal(1); | |
| 44 | 1 | expect(indexOf(['a', undefined, 'b', 'c', 'a'], undefined, 2)).to.equal(-1); | |
| 45 | 1 | done(); | |
| 46 | }); | ||
| 47 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /*! | ||
| 2 | * https://raw.githubusercontent.com/i-voted-for-trump/is-even/0.1.2/test.js | ||
| 3 | */ | ||
| 4 | |||
| 5 | 1 | const Code = require('code'); | |
| 6 | 1 | const Lab = require('lab'); | |
| 7 | 1 | const lab = exports.lab = Lab.script(); | |
| 8 | 1 | const expect = Code.expect; | |
| 9 | 1 | const it = lab.it; | |
| 10 | 1 | const describe = lab.describe; | |
| 11 | |||
| 12 | 1 | const isEven = require('../../../../../helpers/3p/utils/lib/isEven'); | |
| 13 | |||
| 14 | 1 | describe('isEven', function() { | |
| 15 | it('should return true if the number is odd:', function(done) { | ||
| 16 | expect(isEven(0)).to.equal(true); | ||
| 17 | 1 | expect(!isEven(1)).to.equal(true); | |
| 18 | 1 | expect(isEven(2)).to.equal(true); | |
| 19 | 1 | expect(!isEven(3)).to.equal(true); | |
| 20 | 1 | done(); | |
| 21 | }); | ||
| 22 | |||
| 23 | 1 | it('should work with strings:', function(done) { | |
| 24 | expect(isEven('0')).to.equal(true); | ||
| 25 | 1 | expect(!isEven('1')).to.equal(true); | |
| 26 | 1 | expect(isEven('2')).to.equal(true); | |
| 27 | 1 | expect(!isEven('3')).to.equal(true); | |
| 28 | 1 | done(); | |
| 29 | }); | ||
| 30 | |||
| 31 | 1 | it('should throw an error on bad args:', function(done) { | |
| 32 | expect(function() { | ||
| 33 | isEven(); | ||
| 34 | }, /expects a number\.$/).to.throw(); | ||
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it('should throw an error on non-integer args:', function(done) { | |
| 39 | expect(function() { | ||
| 40 | isEven('1.1e0'); | ||
| 41 | }, /expects an integer\.$/).to.throw(); | ||
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | var isExtendable = require('../../../../../helpers/3p/utils/lib/isExtendable'); | |
| 9 | |||
| 10 | |||
| 11 | 1 | describe('isExtendable', function() { | |
| 12 | it('should return true when a value is an object:', function(done) { | ||
| 13 | expect(isExtendable({})).to.equal(true); | ||
| 14 | 1 | expect(isExtendable([])).to.equal(true); | |
| 15 | 1 | expect(isExtendable(function() {})).to.equal(true); | |
| 16 | 1 | done(); | |
| 17 | }); | ||
| 18 | |||
| 19 | 1 | it('should return false when a value is not an object:', function(done) { | |
| 20 | expect(!isExtendable(new RegExp('foo'))).to.equal(true); | ||
| 21 | 1 | expect(!isExtendable(/foo/)).to.equal(true); | |
| 22 | 1 | expect(!isExtendable(new Date())).to.equal(true); | |
| 23 | 1 | expect(!isExtendable(new Error())).to.equal(true); | |
| 24 | 1 | expect(!isExtendable('a')).to.equal(true); | |
| 25 | 1 | expect(!isExtendable(5)).to.equal(true); | |
| 26 | 1 | expect(!isExtendable(null)).to.equal(true); | |
| 27 | 1 | expect(!isExtendable()).to.equal(true); | |
| 28 | 1 | expect(!isExtendable(undefined)).to.equal(true); | |
| 29 | 1 | expect(!isExtendable(true)).to.equal(true); | |
| 30 | 1 | expect(!isExtendable(false)).to.equal(true); | |
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const isNumber = require('../../../../../helpers/3p/utils/lib/isNumber'); | |
| 9 | |||
| 10 | 1 | describe('is a number', function() { | |
| 11 | var fixtures = [ | ||
| 12 | 0xff, | ||
| 13 | 5e3, | ||
| 14 | 0, | ||
| 15 | 0.1, | ||
| 16 | -0.1, | ||
| 17 | -1.1, | ||
| 18 | 37, | ||
| 19 | 3.14, | ||
| 20 | |||
| 21 | 1, | ||
| 22 | 1.1, | ||
| 23 | 10, | ||
| 24 | 10.1, | ||
| 25 | 100, | ||
| 26 | -100, | ||
| 27 | |||
| 28 | '0.1', | ||
| 29 | '-0.1', | ||
| 30 | '-1.1', | ||
| 31 | '0', | ||
| 32 | '012', | ||
| 33 | '0xff', | ||
| 34 | '1', | ||
| 35 | '1.1', | ||
| 36 | '10', | ||
| 37 | '10.10', | ||
| 38 | '100', | ||
| 39 | '5e3', | ||
| 40 | ' 56\r\n ', // issue#3 | ||
| 41 | |||
| 42 | Math.LN2, | ||
| 43 | |||
| 44 | // 012, Octal literal not allowed in strict mode | ||
| 45 | parseInt('012'), | ||
| 46 | parseFloat('012'), | ||
| 47 | Math.abs(1), | ||
| 48 | Math.acos(1), | ||
| 49 | Math.asin(1), | ||
| 50 | Math.atan(1), | ||
| 51 | Math.atan2(1, 2), | ||
| 52 | Math.ceil(1), | ||
| 53 | Math.cos(1), | ||
| 54 | Math.E, | ||
| 55 | Math.exp(1), | ||
| 56 | Math.floor(1), | ||
| 57 | Math.LN10, | ||
| 58 | Math.LN2, | ||
| 59 | Math.log(1), | ||
| 60 | Math.LOG10E, | ||
| 61 | Math.LOG2E, | ||
| 62 | Math.max(1, 2), | ||
| 63 | Math.min(1, 2), | ||
| 64 | Math.PI, | ||
| 65 | Math.pow(1, 2), | ||
| 66 | Math.pow(5, 5), | ||
| 67 | Math.random(1), | ||
| 68 | Math.round(1), | ||
| 69 | Math.sin(1), | ||
| 70 | Math.sqrt(1), | ||
| 71 | Math.SQRT1_2, | ||
| 72 | Math.SQRT2, | ||
| 73 | Math.tan(1), | ||
| 74 | |||
| 75 | Number.MAX_VALUE, | ||
| 76 | Number.MIN_VALUE, | ||
| 77 | |||
| 78 | '0.0', | ||
| 79 | '0x0', | ||
| 80 | '0e+5', | ||
| 81 | '000', | ||
| 82 | '0.0e-5', | ||
| 83 | '0.0E5', | ||
| 84 | |||
| 85 | +'', | ||
| 86 | +1, | ||
| 87 | +3.14, | ||
| 88 | +37, | ||
| 89 | +5, | ||
| 90 | +[], | ||
| 91 | +false, | ||
| 92 | +Math.LN2, | ||
| 93 | +true, | ||
| 94 | +null, | ||
| 95 | +new Date() | ||
| 96 | ]; | ||
| 97 | |||
| 98 | 1 | fixtures.forEach(function(num, idx) { | |
| 99 | it(JSON.stringify(num) + ' should be a number', function(done) { | ||
| 100 | expect(isNumber(num)).to.equal(true); | ||
| 101 | 76 | done(); | |
| 102 | }); | ||
| 103 | }); | ||
| 104 | }); | ||
| 105 | |||
| 106 | 1 | describe('is not a number', function() { | |
| 107 | var fixtures = [ | ||
| 108 | ' ', // issue#3 | ||
| 109 | '\r\n\t', // issue#3 | ||
| 110 | '', | ||
| 111 | '', | ||
| 112 | '3a', | ||
| 113 | 'abc', | ||
| 114 | 'false', | ||
| 115 | 'null', | ||
| 116 | 'true', | ||
| 117 | 'undefined', | ||
| 118 | +'abc', | ||
| 119 | +/foo/, | ||
| 120 | +[1, 2, 4], | ||
| 121 | +Infinity, | ||
| 122 | +Math.sin, | ||
| 123 | +NaN, | ||
| 124 | +undefined, | ||
| 125 | +{ a: 1 }, | ||
| 126 | +{}, | ||
| 127 | /foo/, | ||
| 128 | [1, 2, 3], | ||
| 129 | [1], | ||
| 130 | [], | ||
| 131 | true, | ||
| 132 | false, | ||
| 133 | +function() {}, | ||
| 134 | function() {}, | ||
| 135 | Infinity, | ||
| 136 | -Infinity, | ||
| 137 | Math.sin, | ||
| 138 | NaN, | ||
| 139 | new Date(), | ||
| 140 | null, | ||
| 141 | undefined, | ||
| 142 | {} | ||
| 143 | ]; | ||
| 144 | |||
| 145 | 1 | fixtures.forEach(function(num) { | |
| 146 | it(JSON.stringify(num) + ' should not be a number', function(done) { | ||
| 147 | expect(!isNumber(num)).to.equal(true); | ||
| 148 | 35 | done(); | |
| 149 | }); | ||
| 150 | }); | ||
| 151 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const isOdd = require('../../../../../helpers/3p/utils/lib/isOdd'); | |
| 9 | |||
| 10 | 1 | describe('isOdd', function() { | |
| 11 | it('should return true if the number is odd:', function(done) { | ||
| 12 | expect(!isOdd(0)).to.equal(true); | ||
| 13 | 1 | expect(!isOdd(2)).to.equal(true); | |
| 14 | 1 | expect(isOdd(1)).to.equal(true); | |
| 15 | 1 | expect(isOdd(3)).to.equal(true); | |
| 16 | 1 | expect(isOdd(-1)).to.equal(true); | |
| 17 | 1 | expect(isOdd(-3)).to.equal(true); | |
| 18 | 1 | expect(isOdd(1.0e0)).to.equal(true); | |
| 19 | 1 | expect(isOdd(9007199254740991)).to.equal(true); | |
| 20 | 1 | done(); | |
| 21 | }); | ||
| 22 | |||
| 23 | 1 | it('should work with strings:', function(done) { | |
| 24 | expect(!isOdd('0')).to.equal(true); | ||
| 25 | 1 | expect(!isOdd('2')).to.equal(true); | |
| 26 | 1 | expect(isOdd('1')).to.equal(true); | |
| 27 | 1 | expect(isOdd('3')).to.equal(true); | |
| 28 | 1 | expect(isOdd('1.0e0')).to.equal(true); | |
| 29 | 1 | expect(isOdd('9007199254740991')).to.equal(true); | |
| 30 | 1 | done(); | |
| 31 | }); | ||
| 32 | |||
| 33 | 1 | it('should throw an error when an invalid value is passed', function(done) { | |
| 34 | expect(() => isOdd() , /expected a number/).to.throw(); |
||
| 35 | expect(() => isOdd('foo') , /expected a number/).to.throw(); |
||
| 36 | expect(() => isOdd('1.1e0') , /expected an integer/).to.throw(); |
||
| 37 | // Not being thrown anymore on current Node version | ||
| 38 | // expect(() => isOdd('9007199254740992'), /value exceeds maximum safe integer/).to.throw(); | ||
| 39 | // expect(() => isOdd(9007199254740992), /value exceeds maximum safe integer/).to.throw(); | ||
| 40 | 1 | done(); | |
| 41 | }); | ||
| 42 | }); | ||
| 43 | |||
| 44 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | var isPlainObject = require('../../../../../helpers/3p/utils/lib/isPlainObject'); | |
| 9 | |||
| 10 | 1 | describe('Same-Realm Server Tests', function() { | |
| 11 | it('should return `true` if the object is created by the `Object` constructor.', function(done) { | ||
| 12 | expect(isPlainObject(Object.create({}))).to.equal(true); | ||
| 13 | 1 | expect(isPlainObject(Object.create(Object.prototype))).to.equal(true); | |
| 14 | 1 | expect(isPlainObject({foo: 'bar'})).to.equal(true); | |
| 15 | 1 | expect(isPlainObject({})).to.equal(true); | |
| 16 | 1 | expect(isPlainObject(Object.create(null))).to.equal(true); | |
| 17 | 1 | done(); | |
| 18 | }); | ||
| 19 | |||
| 20 | 1 | it('should return `false` if the object is not created by the `Object` constructor.', function(done) { | |
| 21 | 1 | function Foo() {this.abc = {};}; | |
| 22 | |||
| 23 | 1 | expect(!isPlainObject(/foo/)).to.equal(true); | |
| 24 | 1 | expect(!isPlainObject(function() {})).to.equal(true); | |
| 25 | 1 | expect(!isPlainObject(1)).to.equal(true); | |
| 26 | 1 | expect(!isPlainObject(['foo', 'bar'])).to.equal(true); | |
| 27 | 1 | expect(!isPlainObject([])).to.equal(true); | |
| 28 | 1 | expect(!isPlainObject(new Foo)).to.equal(true); | |
| 29 | 1 | expect(!isPlainObject(null)).to.equal(true); | |
| 30 | 1 | done(); | |
| 31 | }); | ||
| 32 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | /*! | ||
| 2 | * kind-of <https://github.com/jonschlinkert/kind-of> | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014-2015, Jon Schlinkert. | ||
| 5 | * Licensed under the MIT License | ||
| 6 | */ | ||
| 7 | |||
| 8 | 1 | const Code = require('code'); | |
| 9 | 1 | const Lab = require('lab'); | |
| 10 | 1 | const lab = exports.lab = Lab.script(); | |
| 11 | 1 | const expect = Code.expect; | |
| 12 | 1 | const it = lab.it; | |
| 13 | 1 | const describe = lab.describe; | |
| 14 | |||
| 15 | 1 | var kindOf = require('../../../../../helpers/3p/utils/lib/kindOf'); | |
| 16 | |||
| 17 | 1 | var version = process.version.match(/^.(\d+)\.(\d+)/); | |
| 18 | 1 | var major = version[1]; | |
| 19 | 1 | var minor = version[2]; | |
| 20 | |||
| 21 | 1 | describe('kindOf', function () { | |
| 22 | describe('null and undefined', function () { | ||
| 23 | it('should work for undefined', function (done) { | ||
| 24 | expect(kindOf(undefined)).to.equal('undefined'); | ||
| 25 | 1 | done(); | |
| 26 | }); | ||
| 27 | |||
| 28 | 1 | it('should work for null', function (done) { | |
| 29 | expect(kindOf(null)).to.equal('null'); | ||
| 30 | 1 | done(); | |
| 31 | }); | ||
| 32 | }); | ||
| 33 | |||
| 34 | 1 | describe('primitives', function () { | |
| 35 | it('should work for booleans', function (done) { | ||
| 36 | expect(kindOf(true)).to.equal('boolean'); | ||
| 37 | 1 | expect(kindOf(false)).to.equal('boolean'); | |
| 38 | 1 | expect(kindOf(new Boolean(true))).to.equal('boolean'); | |
| 39 | 1 | done(); | |
| 40 | }); | ||
| 41 | |||
| 42 | 1 | it('should work for numbers', function (done) { | |
| 43 | expect(kindOf(42)).to.equal('number'); | ||
| 44 | 1 | expect(kindOf(new Number(42))).to.equal('number'); | |
| 45 | 1 | done(); | |
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it('should work for strings', function (done) { | |
| 49 | expect(kindOf('str')).to.equal('string'); | ||
| 50 | 1 | expect(kindOf(new String('str'))).to.equal('string'); | |
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | }); | ||
| 54 | |||
| 55 | 1 | describe('objects', function () { | |
| 56 | it('should work for arguments', function (done) { | ||
| 57 | (function () { | ||
| 58 | expect(kindOf(arguments)).to.equal('arguments'); | ||
| 59 | 1 | done(); | |
| 60 | })(); | ||
| 61 | }); | ||
| 62 | |||
| 63 | 1 | it('should work for buffers', function (done) { | |
| 64 | expect(kindOf(new Buffer(''))).to.equal('buffer'); | ||
| 65 | 1 | done(); | |
| 66 | }); | ||
| 67 | |||
| 68 | 1 | it('should work for objects', function (done) { | |
| 69 | function Test() {} | ||
| 70 | 1 | var instance = new Test(); | |
| 71 | 1 | var literal = {}; | |
| 72 | 1 | var create = Object.create(null); | |
| 73 | |||
| 74 | 1 | expect(kindOf(instance)).to.equal('object'); | |
| 75 | 1 | expect(kindOf(literal)).to.equal('object'); | |
| 76 | 1 | expect(kindOf(create)).to.equal('object'); | |
| 77 | 1 | done(); | |
| 78 | }); | ||
| 79 | |||
| 80 | 1 | it('should work for dates', function (done) { | |
| 81 | expect(kindOf(new Date())).to.equal('date'); | ||
| 82 | 1 | done(); | |
| 83 | }); | ||
| 84 | |||
| 85 | 1 | it('should work for arrays', function (done) { | |
| 86 | expect(kindOf([])).to.equal('array'); | ||
| 87 | 1 | expect(kindOf([1, 2, 3])).to.equal('array'); | |
| 88 | 1 | expect(kindOf(new Array())).to.equal('array'); | |
| 89 | 1 | done(); | |
| 90 | }); | ||
| 91 | |||
| 92 | 1 | it('should work for regular expressions', function (done) { | |
| 93 | expect(kindOf(/[\s\S]+/)).to.equal('regexp'); | ||
| 94 | 1 | expect(kindOf(new RegExp('^' + 'foo$'))).to.equal('regexp'); | |
| 95 | 1 | done(); | |
| 96 | }); | ||
| 97 | |||
| 98 | 1 | it('should work for functions', function (done) { | |
| 99 | expect(kindOf(function () {})).to.equal('function'); | ||
| 100 | 1 | expect(kindOf(new Function())).to.equal('function'); | |
| 101 | 1 | done(); | |
| 102 | }); | ||
| 103 | }); | ||
| 104 | if ( major > 0 || minor > 11 ) { |
||
| 105 | 1 | describe('es6 features', function () { | |
| 106 | it('should work for Map', function (done) { | ||
| 107 | var map = new Map(); | ||
| 108 | 1 | expect(kindOf(map)).to.equal('map'); | |
| 109 | 1 | expect(kindOf(map.set)).to.equal('function'); | |
| 110 | 1 | expect(kindOf(map.get)).to.equal('function'); | |
| 111 | 1 | expect(kindOf(map.add)).to.equal('undefined'); | |
| 112 | 1 | done(); | |
| 113 | }); | ||
| 114 | |||
| 115 | 1 | it('should work for WeakMap', function (done) { | |
| 116 | var weakmap = new WeakMap(); | ||
| 117 | 1 | expect(kindOf(weakmap)).to.equal('weakmap'); | |
| 118 | 1 | expect(kindOf(weakmap.set)).to.equal('function'); | |
| 119 | 1 | expect(kindOf(weakmap.get)).to.equal('function'); | |
| 120 | 1 | expect(kindOf(weakmap.add)).to.equal('undefined'); | |
| 121 | 1 | done(); | |
| 122 | }); | ||
| 123 | |||
| 124 | 1 | it('should work for Set', function (done) { | |
| 125 | var set = new Set(); | ||
| 126 | 1 | expect(kindOf(set)).to.equal('set'); | |
| 127 | 1 | expect(kindOf(set.add)).to.equal('function'); | |
| 128 | 1 | expect(kindOf(set.set)).to.equal('undefined'); | |
| 129 | 1 | expect(kindOf(set.get)).to.equal('undefined'); | |
| 130 | 1 | done(); | |
| 131 | }); | ||
| 132 | |||
| 133 | 1 | it('should work for WeakSet', function (done) { | |
| 134 | var weakset = new WeakSet(); | ||
| 135 | 1 | expect(kindOf(weakset)).to.equal('weakset'); | |
| 136 | 1 | expect(kindOf(weakset.add)).to.equal('function'); | |
| 137 | 1 | expect(kindOf(weakset.set)).to.equal('undefined'); | |
| 138 | 1 | expect(kindOf(weakset.get)).to.equal('undefined'); | |
| 139 | 1 | done(); | |
| 140 | }); | ||
| 141 | |||
| 142 | 1 | it('should work for Symbol', function (done) { | |
| 143 | expect(kindOf(Symbol('foo'))).to.equal('symbol'); | ||
| 144 | 1 | expect(kindOf(Symbol.prototype)).to.equal('symbol'); | |
| 145 | 1 | done(); | |
| 146 | }); | ||
| 147 | }); | ||
| 148 | } | ||
| 149 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require("code"); | |
| 2 | 1 | const Lab = require("lab"); | |
| 3 | 1 | const lab = (exports.lab = Lab.script()); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | var makeIterator = require("../../../../../helpers/3p/utils/lib/makeIterator"); | |
| 9 | |||
| 10 | 1 | describe('make iterator', function() { | |
| 11 | it('should return source argument if it is already a function with no context', function(done) { | ||
| 12 | var fn = function() {}; | ||
| 13 | 1 | expect(makeIterator(fn)).to.equal(fn); | |
| 14 | 1 | done(); | |
| 15 | }); | ||
| 16 | |||
| 17 | 1 | it('should return a function that calls object/deepMatches if argument is an object', function(done) { | |
| 18 | var fn = makeIterator({ a: 1, b: { c: 2 } }); | ||
| 19 | 1 | expect(fn({ a: 1, b: { c: 2, d: 3 } })).to.equal(true); | |
| 20 | 1 | expect(fn({ a: 1, b: { c: 3 } })).to.equal(false); | |
| 21 | 1 | done(); | |
| 22 | }); | ||
| 23 | |||
| 24 | 1 | it('should return a function that calls object/deepMatches if argument is a regex', function(done) { | |
| 25 | expect(makeIterator(/[a-c]/)(['a', 'b', 'c', 'd'])).to.equal(true); | ||
| 26 | 1 | expect(makeIterator(/[m-z]/)(['a', 'b', 'c', 'd'])).to.equal(false); | |
| 27 | 1 | done(); | |
| 28 | }); | ||
| 29 | |||
| 30 | 1 | it('should return a function that returns the property value if argument is a string', function(done) { | |
| 31 | var fn = makeIterator('a'); | ||
| 32 | 1 | expect(fn({a:1,b:2})).to.equal(1); | |
| 33 | 1 | expect(fn({a:2,b:2})).to.equal(2); | |
| 34 | 1 | done(); | |
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should return a function that returns the property value if argument is a number', function(done) { | |
| 38 | var fn = makeIterator(1); | ||
| 39 | 1 | expect(fn([0,4,5])).to.equal(4); | |
| 40 | 1 | expect(fn([6,7,8])).to.equal(7); | |
| 41 | 1 | done(); | |
| 42 | }); | ||
| 43 | |||
| 44 | 1 | it('should return an identify function if no args', function(done) { | |
| 45 | var fn = makeIterator(); | ||
| 46 | 1 | expect(fn(null)).to.equal(null); | |
| 47 | 1 | expect(fn(void(0))).to.equal(void(0)); | |
| 48 | 1 | expect(fn(3)).to.equal(3); | |
| 49 | 1 | done(); | |
| 50 | }); | ||
| 51 | |||
| 52 | 1 | it('should return an identify function if first arg is `null`', function(done) { | |
| 53 | var fn = makeIterator(null); | ||
| 54 | 1 | expect(fn(null)).to.equal(null); | |
| 55 | 1 | expect(fn(void(0))).to.equal(void(0)); | |
| 56 | 1 | expect(fn(3)).to.equal(3); | |
| 57 | 1 | done(); | |
| 58 | }); | ||
| 59 | |||
| 60 | 1 | it('should return a function that is called with the specified context', function(done) { | |
| 61 | var context = {}; | ||
| 62 | 1 | var iterator = makeIterator(function() { return this; }, context); | |
| 63 | 1 | expect(iterator()).to.equal(context); | |
| 64 | 1 | done(); | |
| 65 | }); | ||
| 66 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require("code"); | |
| 2 | 1 | const Lab = require("lab"); | |
| 3 | 1 | const lab = (exports.lab = Lab.script()); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const markdown = require("../../../../../helpers/3p/utils/lib/markdown"); | |
| 9 | |||
| 10 | 1 | const {buildRenderer} = require("../../../../spec-helpers"); | |
| 11 | 1 | const renderer = buildRenderer(); | |
| 12 | 1 | const hbs = renderer.handlebars; | |
| 13 | |||
| 14 | 1 | const hljs = require("highlight.js"); | |
| 15 | |||
| 16 | function highlight(code, language) { | ||
| 17 | 4 | try { | |
| 18 | 4 | try { | |
| 19 | 4 | return hljs.highlight(code, {language}).value; | |
| 20 | } catch (err) { | ||
| 21 | if (!/Unknown language/i.test(err.message)) { | ||
| 22 | throw err; | ||
| 23 | } | ||
| 24 | return hljs.highlightAuto(code).value; | ||
| 25 | } | ||
| 26 | } catch (err) { | ||
| 27 | return code; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | 1 | describe("sync", function () { | |
| 32 | describe("markdown helper", function () { | ||
| 33 | it("should render markdown:", function (done) { | ||
| 34 | expect(markdown("# heading")).to.equal("<h1>heading</h1>\n"); | ||
| 35 | 1 | done(); | |
| 36 | }); | ||
| 37 | |||
| 38 | 1 | it("should highlight code blocks", function (done) { | |
| 39 | const html = markdown('```js\nvar foo = "bar";\n```\n', { | ||
| 40 | highlight: highlight, | ||
| 41 | }); | ||
| 42 | 1 | expect(html).to.equal( | |
| 43 | '<pre><code class="language-js"><span class="hljs-keyword">var</span> foo = <span class="hljs-string">"bar"</span>;\n</code></pre>\n' | ||
| 44 | ); | ||
| 45 | 1 | done(); | |
| 46 | }); | ||
| 47 | |||
| 48 | 1 | it("should pass options to remarkable", function (done) { | |
| 49 | const a = markdown("abc https://github.com/jonschlinkert/remarkable xyz", { | ||
| 50 | highlight: highlight, | ||
| 51 | linkify: true, | ||
| 52 | }); | ||
| 53 | 1 | expect(a).to.equal( | |
| 54 | '<p>abc <a href="https://github.com/jonschlinkert/remarkable">https://github.com/jonschlinkert/remarkable</a> xyz</p>\n' | ||
| 55 | ); | ||
| 56 | |||
| 57 | 1 | const b = markdown("abc https://github.com/jonschlinkert/remarkable xyz", { | |
| 58 | highlight: highlight, | ||
| 59 | linkify: false, | ||
| 60 | }); | ||
| 61 | 1 | expect(b).to.equal( | |
| 62 | "<p>abc https://github.com/jonschlinkert/remarkable xyz</p>\n" | ||
| 63 | ); | ||
| 64 | 1 | done(); | |
| 65 | }); | ||
| 66 | |||
| 67 | 1 | it("should pass options to highlight.js:", function (done) { | |
| 68 | const html = markdown('```js\nvar foo = "bar";\n```\n', { | ||
| 69 | highlight: highlight, | ||
| 70 | langPrefix: "language-", | ||
| 71 | }); | ||
| 72 | 1 | expect(html).to.equal( | |
| 73 | '<pre><code class="language-js"><span class="hljs-keyword">var</span> foo = <span class="hljs-string">"bar"</span>;\n</code></pre>\n' | ||
| 74 | ); | ||
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | describe("handlebars:", function () { | |
| 80 | it("should work as a handlebars helper:", function (done) { | ||
| 81 | hbs.registerHelper("markdown", markdown({highlight: highlight})); | ||
| 82 | 1 | expect( | |
| 83 | hbs.compile("{{#markdown}}# {{title}}{{/markdown}}")({ | ||
| 84 | title: "heading", | ||
| 85 | }) | ||
| 86 | ).to.equal("<h1>heading</h1>\n"); | ||
| 87 | 1 | done(); | |
| 88 | }); | ||
| 89 | |||
| 90 | 1 | it("should pass hash options to remarkable:", function (done) { | |
| 91 | hbs.registerHelper("markdown", markdown({highlight: highlight})); | ||
| 92 | |||
| 93 | // `linkify: true` | ||
| 94 | 1 | const a = hbs.compile( | |
| 95 | "{{#markdown linkify=true}}abc https://github.com/jonschlinkert/remarkable xyz{{/markdown}}" | ||
| 96 | )(); | ||
| 97 | 1 | expect(a).to.equal( | |
| 98 | '<p>abc <a href="https://github.com/jonschlinkert/remarkable">https://github.com/jonschlinkert/remarkable</a> xyz</p>\n' | ||
| 99 | ); | ||
| 100 | |||
| 101 | // `linkify: false` | ||
| 102 | 1 | const b = hbs.compile( | |
| 103 | "{{#markdown linkify=false}}abc https://github.com/jonschlinkert/remarkable xyz{{/markdown}}" | ||
| 104 | )(); | ||
| 105 | 1 | expect(b).to.equal( | |
| 106 | "<p>abc https://github.com/jonschlinkert/remarkable xyz</p>\n" | ||
| 107 | ); | ||
| 108 | 1 | done(); | |
| 109 | }); | ||
| 110 | |||
| 111 | 1 | it("should pass hash options to highlight.js:", function (done) { | |
| 112 | hbs.registerHelper("markdown", markdown({highlight: highlight})); | ||
| 113 | |||
| 114 | // `langPrefix = language-` | ||
| 115 | 1 | const a = hbs.compile( | |
| 116 | '{{#markdown}}```js\nvar foo = "bar";\n```\n{{/markdown}}' | ||
| 117 | )(); | ||
| 118 | 1 | expect(a).to.equal( | |
| 119 | '<pre><code class="language-js"><span class="hljs-keyword">var</span> foo = <span class="hljs-string">"bar"</span>;\n</code></pre>\n' | ||
| 120 | ); | ||
| 121 | |||
| 122 | // `langPrefix = language-` | ||
| 123 | 1 | const b = hbs.compile( | |
| 124 | '{{#markdown langPrefix="language-"}}```js\nvar foo = "bar";\n```\n{{/markdown}}' | ||
| 125 | )(); | ||
| 126 | 1 | expect(b).to.equal( | |
| 127 | '<pre><code class="language-js"><span class="hljs-keyword">var</span> foo = <span class="hljs-string">"bar"</span>;\n</code></pre>\n' | ||
| 128 | ); | ||
| 129 | 1 | done(); | |
| 130 | }); | ||
| 131 | }); | ||
| 132 | }); | ||
| 133 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | var mixinDeep = require('../../../../../helpers/3p/utils/lib/mixinDeep'); | |
| 9 | |||
| 10 | |||
| 11 | 1 | describe('.mixinDeep()', function() { | |
| 12 | it('should deeply mix the properties of object into the first object.', function(done) { | ||
| 13 | var a = mixinDeep({a: {aa: 'aa'} }, {a: {bb: 'bb'} }, {a: {cc: 'cc'} }); | ||
| 14 | 1 | expect(a).to.equal({a: {aa: 'aa', bb: 'bb', cc: 'cc'} }); | |
| 15 | 1 | var b = mixinDeep({a: {aa: 'aa', dd: {ee: 'ff'} } }, {a: {bb: 'bb', dd: {gg: 'hh'} } }, {a: {cc: 'cc', dd: {ii: 'jj'} } }); | |
| 16 | 1 | expect(b).to.equal({a: {aa: 'aa', dd: {ee: 'ff', gg: 'hh', ii: 'jj'}, bb: 'bb', cc: 'cc'} }); | |
| 17 | 1 | done(); | |
| 18 | }); | ||
| 19 | |||
| 20 | 1 | it('should merge object properties without affecting any object', function(done) { | |
| 21 | var obj1 = {a: 0, b: 1}; | ||
| 22 | 1 | var obj2 = {c: 2, d: 3}; | |
| 23 | 1 | var obj3 = {a: 4, d: 5}; | |
| 24 | |||
| 25 | 1 | var actual = {a: 4, b: 1, c: 2, d: 5 }; | |
| 26 | |||
| 27 | 1 | expect(mixinDeep({}, obj1, obj2, obj3)).to.equal(actual); | |
| 28 | 1 | expect(actual).to.not.equal(obj1); | |
| 29 | 1 | expect(actual).to.not.equal(obj2); | |
| 30 | 1 | expect(actual).to.not.equal(obj3); | |
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('should do a deep merge', function(done) { | |
| 35 | var obj1 = {a: {b: 1, c: 1, d: {e: 1, f: 1}}}; | ||
| 36 | 1 | var obj2 = {a: {b: 2, d : {f : 'f'} }}; | |
| 37 | |||
| 38 | 1 | expect(mixinDeep(obj1, obj2)).to.equal({a: {b: 2, c: 1, d: {e: 1, f: 'f'} }}); | |
| 39 | 1 | done(); | |
| 40 | }); | ||
| 41 | |||
| 42 | 1 | it('should use the last value defined', function(done) { | |
| 43 | var obj1 = {a: 'b'}; | ||
| 44 | 1 | var obj2 = {a: 'c'}; | |
| 45 | |||
| 46 | 1 | expect(mixinDeep(obj1, obj2)).to.equal({a: 'c'}); | |
| 47 | 1 | done(); | |
| 48 | }); | ||
| 49 | |||
| 50 | 1 | it('should use the last value defined on nested object', function(done) { | |
| 51 | var obj1 = {a: 'b', c: {d: 'e'}}; | ||
| 52 | 1 | var obj2 = {a: 'c', c: {d: 'f'}}; | |
| 53 | |||
| 54 | 1 | expect(mixinDeep(obj1, obj2)).to.equal({a: 'c', c: {d: 'f'}}); | |
| 55 | 1 | done(); | |
| 56 | }); | ||
| 57 | |||
| 58 | 1 | it('should shallow clone when an empty object is passed', function(done) { | |
| 59 | var obj1 = {a: 'b', c: {d: 'e'}}; | ||
| 60 | 1 | var obj2 = {a: 'c', c: {d: 'f'}}; | |
| 61 | |||
| 62 | 1 | var res = mixinDeep({}, obj1, obj2); | |
| 63 | 1 | expect(res).to.equal({a: 'c', c: {d: 'f'}}); | |
| 64 | 1 | done(); | |
| 65 | }); | ||
| 66 | |||
| 67 | 1 | it('should merge additional objects into the first:', function(done) { | |
| 68 | var obj1 = {a: {b: 1, c: 1, d: {e: 1, f: 1}}}; | ||
| 69 | 1 | var obj2 = {a: {b: 2, d : {f : 'f'} }}; | |
| 70 | |||
| 71 | 1 | mixinDeep(obj1, obj2); | |
| 72 | 1 | expect(obj1).to.equal({a: {b: 2, c: 1, d: {e: 1, f: 'f'} }}); | |
| 73 | 1 | done(); | |
| 74 | }); | ||
| 75 | |||
| 76 | 1 | it('should clone objects during merge', function(done) { | |
| 77 | var obj1 = {a: {b :1}}; | ||
| 78 | 1 | var obj2 = {a: {c :2}}; | |
| 79 | |||
| 80 | 1 | var actual = mixinDeep({}, obj1, obj2); | |
| 81 | 1 | expect(actual).to.equal({a:{b:1, c:2}}); | |
| 82 | 1 | expect(actual.a).to.equal(obj1.a); | |
| 83 | 1 | expect(actual.a).to.not.equal(obj2.a); | |
| 84 | 1 | done(); | |
| 85 | }); | ||
| 86 | |||
| 87 | 1 | it('should deep clone arrays during merge', function(done) { | |
| 88 | var obj1 = {a: [1, 2, [3, 4]]}; | ||
| 89 | 1 | var obj2 = {b : [5, 6]}; | |
| 90 | |||
| 91 | 1 | var actual = mixinDeep(obj1, obj2); | |
| 92 | 1 | expect(actual.a).to.equal([1, 2, [3, 4]]); | |
| 93 | 1 | expect(actual.a[2]).to.equal([3, 4]); | |
| 94 | 1 | expect(actual.b).to.equal(obj2.b); | |
| 95 | 1 | done(); | |
| 96 | }); | ||
| 97 | |||
| 98 | 1 | it('should copy source properties', function(done) { | |
| 99 | expect(mixinDeep({ test: true }).test).to.equal(true); | ||
| 100 | 1 | done(); | |
| 101 | }); | ||
| 102 | |||
| 103 | 1 | it('should not clone arrays', function(done) { | |
| 104 | expect(mixinDeep([1, 2, 3])).to.equal([1, 2, 3]); | ||
| 105 | 1 | expect(mixinDeep([1, 2, 3], {})).to.equal([1, 2, 3]); | |
| 106 | 1 | done(); | |
| 107 | }); | ||
| 108 | |||
| 109 | 1 | it('should work with sparse objects:', function(done) { | |
| 110 | var actual = mixinDeep({}, undefined, {a: 'b'}, undefined, {c: 'd'}); | ||
| 111 | 1 | expect(actual).to.equal({a: 'b', c: 'd'}); | |
| 112 | 1 | done(); | |
| 113 | }); | ||
| 114 | |||
| 115 | 1 | it('should clone RegExps', function(done) { | |
| 116 | var fixture = /test/g; | ||
| 117 | 1 | var actual = mixinDeep(fixture); | |
| 118 | 1 | expect(actual).to.equal(fixture); | |
| 119 | 1 | done(); | |
| 120 | }); | ||
| 121 | |||
| 122 | 1 | it('should clone Dates', function(done) { | |
| 123 | var fixture = new Date(); | ||
| 124 | 1 | var actual = mixinDeep(fixture); | |
| 125 | 1 | expect(actual).to.equal(fixture); | |
| 126 | 1 | done(); | |
| 127 | }); | ||
| 128 | |||
| 129 | 1 | it('should not clone objects created with custom constructor', function(done) { | |
| 130 | function TestType() { } | ||
| 131 | 1 | var fixture = new TestType(); | |
| 132 | 1 | var actual = mixinDeep(fixture); | |
| 133 | 1 | expect(actual).to.equal(fixture); | |
| 134 | 1 | done(); | |
| 135 | }); | ||
| 136 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'); | |
| 2 | 1 | const Lab = require('lab'); | |
| 3 | 1 | const lab = exports.lab = Lab.script(); | |
| 4 | 1 | const expect = Code.expect; | |
| 5 | 1 | const it = lab.it; | |
| 6 | 1 | const describe = lab.describe; | |
| 7 | |||
| 8 | 1 | const striptags = require('../../../../../helpers/3p/utils/lib/striptags'); | |
| 9 | |||
| 10 | 1 | describe('striptags', function() { | |
| 11 | it('should not modify plain text', function(done) { | ||
| 12 | var text = 'lorem ipsum < a>'; | ||
| 13 | |||
| 14 | 1 | expect(striptags(text)).to.equal(text); | |
| 15 | 1 | done(); | |
| 16 | }); | ||
| 17 | |||
| 18 | 1 | it('should remove simple HTML tags', function(done) { | |
| 19 | var html = '<a href="">lorem <strong>ipsum</strong></a>', | ||
| 20 | text = 'lorem ipsum'; | ||
| 21 | |||
| 22 | 1 | expect(striptags(html)).to.equal(text); | |
| 23 | 1 | done(); | |
| 24 | }); | ||
| 25 | |||
| 26 | 1 | it('should leave HTML tags if specified', function(done) { | |
| 27 | var html = '<strong>lorem ipsum</strong>', | ||
| 28 | allowedTags = '<strong>'; | ||
| 29 | |||
| 30 | 1 | expect(striptags(html, allowedTags)).to.equal(html); | |
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('should leave attributes when allowing HTML', function(done) { | |
| 35 | var html = '<a href="https://example.com">lorem ipsum</a>', | ||
| 36 | allowedTags = '<a>'; | ||
| 37 | |||
| 38 | 1 | expect(striptags(html, allowedTags)).to.equal(html); | |
| 39 | 1 | done(); | |
| 40 | }); | ||
| 41 | |||
| 42 | 1 | it('should leave nested HTML tags if specified', function(done) { | |
| 43 | var html = '<div>lorem <strong>ipsum</strong></div>', | ||
| 44 | strippedHtml = 'lorem <strong>ipsum</strong>', | ||
| 45 | allowedTags = '<strong>'; | ||
| 46 | |||
| 47 | 1 | expect(striptags(html, allowedTags)).to.equal(strippedHtml); | |
| 48 | 1 | done(); | |
| 49 | }); | ||
| 50 | |||
| 51 | 1 | it('should leave outer HTML tags if specified', function(done) { | |
| 52 | var html = '<div>lorem <strong>ipsum</strong></div>', | ||
| 53 | strippedHtml = '<div>lorem ipsum</div>', | ||
| 54 | allowedTags = '<div>'; | ||
| 55 | |||
| 56 | 1 | expect(striptags(html, allowedTags)).to.equal(strippedHtml); | |
| 57 | 1 | done(); | |
| 58 | }); | ||
| 59 | |||
| 60 | 1 | it('should remove DOCTYPE declaration', function(done) { | |
| 61 | var html = '<!DOCTYPE html> lorem ipsum', | ||
| 62 | text = ' lorem ipsum'; | ||
| 63 | |||
| 64 | 1 | expect(striptags(html)).to.equal(text); | |
| 65 | 1 | done(); | |
| 66 | }); | ||
| 67 | |||
| 68 | 1 | it('should remove comments', function(done) { | |
| 69 | var html = '<!-- lorem ipsum --> dolor sit amet', | ||
| 70 | text = ' dolor sit amet'; | ||
| 71 | |||
| 72 | 1 | expect(striptags(html)).to.equal(text); | |
| 73 | 1 | done(); | |
| 74 | }); | ||
| 75 | |||
| 76 | 1 | it('should strip <> within quotes', function(done) { | |
| 77 | var html = '<a href="<script>">lorem ipsum</a>', | ||
| 78 | strippedHtml = '<a href="script">lorem ipsum</a>', | ||
| 79 | allowedTags = '<a>'; | ||
| 80 | |||
| 81 | 1 | expect(striptags(html, allowedTags)).to.equal(strippedHtml); | |
| 82 | 1 | done(); | |
| 83 | }); | ||
| 84 | |||
| 85 | 1 | it('should strip extra < within tags', function(done) { | |
| 86 | var html = '<div<>>lorem ipsum</div>', | ||
| 87 | strippedHtml = '<div>lorem ipsum</div>', | ||
| 88 | allowedTags = '<div>'; | ||
| 89 | |||
| 90 | 1 | expect(striptags(html, allowedTags)).to.equal(strippedHtml); | |
| 91 | 1 | done(); | |
| 92 | }); | ||
| 93 | |||
| 94 | 1 | it('should strip tags within comments', function(done) { | |
| 95 | var html = '<!-- <strong>lorem ipsum</strong> --> dolor sit', | ||
| 96 | text = ' dolor sit'; | ||
| 97 | |||
| 98 | 1 | expect(striptags(html)).to.equal(text); | |
| 99 | 1 | done(); | |
| 100 | }); | ||
| 101 | |||
| 102 | 1 | it('should strip comment-like tags', function(done) { | |
| 103 | var html = '<! lorem ipsum> dolor sit', | ||
| 104 | text = ' dolor sit'; | ||
| 105 | |||
| 106 | 1 | expect(striptags(html)).to.equal(text); | |
| 107 | 1 | done(); | |
| 108 | }); | ||
| 109 | |||
| 110 | 1 | it('should leave normal exclamation points alone', function(done) { | |
| 111 | var text = 'lorem ipsum! dolor sit amet'; | ||
| 112 | |||
| 113 | 1 | expect(striptags(text)).to.equal(text); | |
| 114 | 1 | done(); | |
| 115 | }); | ||
| 116 | |||
| 117 | 1 | it('should allow an array parameter for allowable tags', function(done) { | |
| 118 | var html = '<strong>lorem <em>ipsum</em></strong>', | ||
| 119 | allowedTags = ['strong', 'em']; | ||
| 120 | |||
| 121 | 1 | expect(striptags(html, allowedTags)).to.equal(html); | |
| 122 | 1 | done(); | |
| 123 | }); | ||
| 124 | |||
| 125 | 1 | it('should strip tags when an empty array is provided', function(done) { | |
| 126 | var html = '<article>lorem <a href="#">ipsum</a></article>', | ||
| 127 | allowedTags = [], | ||
| 128 | text = 'lorem ipsum'; | ||
| 129 | |||
| 130 | 1 | expect(striptags(html, allowedTags)).to.equal(text); | |
| 131 | 1 | done(); | |
| 132 | }); | ||
| 133 | |||
| 134 | 1 | it('should not fail with nested quotes', function(done) { | |
| 135 | var html = '<article attr="foo \'bar\'">lorem</article> ipsum', | ||
| 136 | allowedTags = [], | ||
| 137 | text = 'lorem ipsum'; | ||
| 138 | |||
| 139 | 1 | expect(striptags(html, allowedTags)).to.equal(text); | |
| 140 | 1 | done(); | |
| 141 | }); | ||
| 142 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const getValue = require('../../../helpers/lib/common').getValue; | |
| 2 | 1 | const appendLossyParam = require('../../../helpers/lib/common').appendLossyParam; | |
| 3 | 1 | const Code = require('code'), | |
| 4 | expect = Code.expect; | ||
| 5 | 1 | const Lab = require('lab'), | |
| 6 | lab = exports.lab = Lab.script(), | ||
| 7 | describe = lab.experiment, | ||
| 8 | it = lab.it; | ||
| 9 | 1 | const Handlebars = require('handlebars'); | |
| 10 | |||
| 11 | 1 | describe('common utils', function () { | |
| 12 | describe('getValue', function () { | ||
| 13 | const globals = {handlebars: Handlebars}; | ||
| 14 | 1 | const obj = { | |
| 15 | a: { | ||
| 16 | a: [{x: 'a'}, {y: 'b'}], | ||
| 17 | b: { | ||
| 18 | b: { | ||
| 19 | a: 1, | ||
| 20 | }, | ||
| 21 | }, | ||
| 22 | c: [1, 1, 2, 3, 5, 8, 13, 21, 34], | ||
| 23 | }, | ||
| 24 | b: [2, 3, 5, 7, 11, 13, 17, 19], | ||
| 25 | c: 3, | ||
| 26 | d: false, | ||
| 27 | '42': 42, | ||
| 28 | }; | ||
| 29 | 1 | obj.__proto__ = {x: 'yz'}; | |
| 30 | |||
| 31 | 1 | it('should get a value from an object', (done) => { | |
| 32 | expect(getValue(obj, 'c', globals)).to.equal(3); | ||
| 33 | 1 | expect(getValue(obj, 'd', globals)).to.equal(false); | |
| 34 | 1 | done(); | |
| 35 | }); | ||
| 36 | |||
| 37 | 1 | it('should get nested values', (done) => { | |
| 38 | expect(getValue(obj, 'a.b.b.a', globals)).to.equal(1); | ||
| 39 | 1 | expect(getValue(obj, ['a', 'b', 'b', 'a'], globals)).to.equal(1); | |
| 40 | 1 | done(); | |
| 41 | }); | ||
| 42 | |||
| 43 | 1 | it('should get nested values from arrays', (done) => { | |
| 44 | expect(getValue(obj, 'b.0', globals)).to.equal(2); | ||
| 45 | 1 | expect(getValue(obj, 'a.c.5', globals)).to.equal(8); | |
| 46 | 1 | done(); | |
| 47 | }); | ||
| 48 | |||
| 49 | 1 | it('should get nested values from objects in arrays', (done) => { | |
| 50 | expect(getValue(obj, 'a.a.1.y', globals)).to.equal('b'); | ||
| 51 | 1 | done(); | |
| 52 | }); | ||
| 53 | |||
| 54 | 1 | it('should return obj[String(path)] or undefined if path is not a string or array', (done) => { | |
| 55 | expect(getValue(obj, {a: 1}, globals)).to.equal(undefined); | ||
| 56 | 1 | expect(getValue(obj, ()=>1, globals)).to.equal(undefined); | |
| 57 | 1 | expect(getValue(obj, 42, globals)).to.equal(42); | |
| 58 | 1 | done(); | |
| 59 | }); | ||
| 60 | |||
| 61 | 1 | it('should return the whole object if path is empty', (done) => { | |
| 62 | expect(getValue(obj, [], globals)).to.equal(obj); | ||
| 63 | 1 | done(); | |
| 64 | }); | ||
| 65 | |||
| 66 | 1 | it('should return obj if path is falsey', (done) => { | |
| 67 | expect(getValue(obj, '', globals)).to.equal(obj); | ||
| 68 | 1 | expect(getValue(obj, false, globals)).to.equal(obj); | |
| 69 | 1 | expect(getValue(obj, 0, globals)).to.equal(obj); | |
| 70 | 1 | done(); | |
| 71 | }); | ||
| 72 | |||
| 73 | 1 | it('should return undefined if prop does not exist', (done) => { | |
| 74 | expect(getValue(obj, 'a.a.a.a', globals)).to.equal(undefined); | ||
| 75 | 1 | expect(getValue(obj, 'a.c.23', globals)).to.equal(undefined); | |
| 76 | 1 | expect(getValue(obj, 'ab', globals)).to.equal(undefined); | |
| 77 | 1 | expect(getValue(obj, 'nonexistent', globals)).to.equal(undefined); | |
| 78 | 1 | expect(getValue([ undefined ], '0.x', globals)).to.equal(undefined); | |
| 79 | 1 | expect(getValue([ null ], '0.x', globals)).to.equal(undefined); | |
| 80 | 1 | done(); | |
| 81 | }); | ||
| 82 | |||
| 83 | 1 | it('should treat backslash-escaped . characters as part of a prop name', (done) => { | |
| 84 | const data = {'a.b': {'c.d.e': 42, z: 'xyz'}}; | ||
| 85 | |||
| 86 | 1 | expect(getValue(data, 'a\\.b.z', globals)).to.equal('xyz'); | |
| 87 | 1 | expect(getValue(data, 'a\\.b.c\\.d\\.e', globals)).to.equal(42); | |
| 88 | 1 | done() | |
| 89 | }); | ||
| 90 | |||
| 91 | 1 | it('should not access inherited props', (done) => { | |
| 92 | expect(getValue(obj, 'x', globals)).to.equal(undefined); | ||
| 93 | 1 | expect(getValue(obj, 'a.constructor', globals)).to.equal(undefined); | |
| 94 | 1 | done(); | |
| 95 | }); | ||
| 96 | }); | ||
| 97 | |||
| 98 | 1 | describe('appendLossyParam', function() { | |
| 99 | it('should append compression=lossy to URLs without query params', function(done) { | ||
| 100 | const result = appendLossyParam('https://example.com/image.jpg', true); | ||
| 101 | 1 | expect(result).to.equal('https://example.com/image.jpg?compression=lossy'); | |
| 102 | 1 | done(); | |
| 103 | }); | ||
| 104 | |||
| 105 | 1 | it('should append compression=lossy to URLs with existing query params', function(done) { | |
| 106 | const result = appendLossyParam('https://example.com/image.jpg?c=2', true); | ||
| 107 | 1 | expect(result).to.equal('https://example.com/image.jpg?c=2&compression=lossy'); | |
| 108 | 1 | done(); | |
| 109 | }); | ||
| 110 | |||
| 111 | 1 | it('should not modify URL when lossy is false', function(done) { | |
| 112 | const url = 'https://example.com/image.jpg?c=2'; | ||
| 113 | 1 | const result = appendLossyParam(url, false); | |
| 114 | 1 | expect(result).to.equal(url); | |
| 115 | 1 | done(); | |
| 116 | }); | ||
| 117 | |||
| 118 | 1 | it('should not modify URL when lossy is undefined', function(done) { | |
| 119 | const url = 'https://example.com/image.jpg'; | ||
| 120 | 1 | const result = appendLossyParam(url); | |
| 121 | 1 | expect(result).to.equal(url); | |
| 122 | 1 | done(); | |
| 123 | }); | ||
| 124 | |||
| 125 | 1 | it('should not modify URL when lossy is not a boolean', function(done) { | |
| 126 | const url = 'https://example.com/image.jpg'; | ||
| 127 | 1 | const result = appendLossyParam(url, 'true'); | |
| 128 | 1 | expect(result).to.equal(url); | |
| 129 | 1 | done(); | |
| 130 | }); | ||
| 131 | }); | ||
| 132 | }); |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 1 | const Code = require('code'), | |
| 2 | expect = Code.expect; | ||
| 3 | 1 | const Lab = require('lab'), | |
| 4 | lab = exports.lab = Lab.script(), | ||
| 5 | describe = lab.experiment, | ||
| 6 | it = lab.it; | ||
| 7 | 1 | const { | |
| 8 | addResourceHint, | ||
| 9 | defaultResourceHintState, | ||
| 10 | resourceHintAllowedTypes | ||
| 11 | } = require('../../../helpers/lib/resourceHints'); | ||
| 12 | |||
| 13 | 1 | describe('resource hints', function () { | |
| 14 | |||
| 15 | describe('addResourceHint', function () { | ||
| 16 | |||
| 17 | it("creates resource hints when valid params are provided", (done) => { | ||
| 18 | const globals = {resourceHints: []}; | ||
| 19 | |||
| 20 | 1 | const src = '/my/styles.css'; | |
| 21 | 1 | const type = 'style'; | |
| 22 | 1 | const state = 'preload'; | |
| 23 | 1 | const cors = 'anonymous'; | |
| 24 | 1 | const expected = {src, state, type, cors}; | |
| 25 | |||
| 26 | 1 | addResourceHint(globals, src, state, type, cors); | |
| 27 | |||
| 28 | 1 | expect(globals.resourceHints).to.have.length(1); | |
| 29 | 1 | expect(globals.resourceHints[0]).to.equals(expected); | |
| 30 | |||
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it("creates resource hints when valid params are provided defaulting cors to no when no provided", (done) => { | |
| 35 | const globals = {}; | ||
| 36 | |||
| 37 | 1 | const src = '/my/styles.css'; | |
| 38 | 1 | const type = 'style'; | |
| 39 | 1 | const state = 'preload'; | |
| 40 | 1 | const expected = {src, state, type, cors: 'no'}; | |
| 41 | |||
| 42 | 1 | addResourceHint(globals, src, state, type); | |
| 43 | |||
| 44 | 1 | expect(globals.resourceHints).to.have.length(1); | |
| 45 | 1 | expect(globals.resourceHints[0]).to.equals(expected); | |
| 46 | |||
| 47 | 1 | done(); | |
| 48 | }); | ||
| 49 | |||
| 50 | 1 | it('does not add a hint when provided path is invalid', (done) => { | |
| 51 | const globals = {resourceHints: []}; | ||
| 52 | |||
| 53 | 1 | const throws = [undefined, null, ''].map(s => { | |
| 54 | return () => { | ||
| 55 | addResourceHint( | ||
| 56 | global, | ||
| 57 | s, | ||
| 58 | defaultResourceHintState, | ||
| 59 | resourceHintAllowedTypes.resourceHintStyleType | ||
| 60 | ); | ||
| 61 | }; | ||
| 62 | }); | ||
| 63 | |||
| 64 | 1 | throws.forEach(t => { | |
| 65 | expect(t).to.throw(); | ||
| 66 | }); | ||
| 67 | |||
| 68 | 1 | expect(globals.resourceHints).to.have.length(0); | |
| 69 | |||
| 70 | 1 | done(); | |
| 71 | }); | ||
| 72 | |||
| 73 | 1 | it('does create a hint when no type is provided', (done) => { | |
| 74 | const globals = {resourceHints: []}; | ||
| 75 | |||
| 76 | 1 | addResourceHint( | |
| 77 | globals, | ||
| 78 | 'https://my.asset.css', | ||
| 79 | defaultResourceHintState | ||
| 80 | ); | ||
| 81 | |||
| 82 | 1 | expect(globals.resourceHints).to.have.length(1); | |
| 83 | 1 | expect(globals.resourceHints[0].type).to.not.exist(); | |
| 84 | |||
| 85 | 1 | done(); | |
| 86 | }); | ||
| 87 | |||
| 88 | 1 | it('does not create a hint when provided state param is not supported', (done) => { | |
| 89 | const globals = {resourceHints: []}; | ||
| 90 | |||
| 91 | const f = () => addResourceHint( |
||
| 92 | globals, | ||
| 93 | '/styles.css', | ||
| 94 | 'not-supported' | ||
| 95 | ) ; |
||
| 96 | |||
| 97 | 1 | expect(f).to.throw(); | |
| 98 | |||
| 99 | 1 | done(); | |
| 100 | }); | ||
| 101 | |||
| 102 | 1 | it('does not create duplicate, by src, hints, and returns the found src', (done) => { | |
| 103 | const globals = {resourceHints: []}; | ||
| 104 | |||
| 105 | 1 | const src = '/my/styles.css'; | |
| 106 | 1 | const type = 'style'; | |
| 107 | 1 | const state = 'preload'; | |
| 108 | |||
| 109 | 1 | const first = addResourceHint( | |
| 110 | globals, | ||
| 111 | src, | ||
| 112 | state, | ||
| 113 | type | ||
| 114 | ); | ||
| 115 | |||
| 116 | 1 | for (let i = 0; i < 5; i++) { | |
| 117 | 5 | const found = addResourceHint( | |
| 118 | globals, | ||
| 119 | src, | ||
| 120 | state, | ||
| 121 | type | ||
| 122 | ); | ||
| 123 | 5 | expect(found).to.equals(first); | |
| 124 | } | ||
| 125 | |||
| 126 | 1 | expect(globals.resourceHints).to.have.length(1); | |
| 127 | |||
| 128 | 1 | done(); | |
| 129 | }); | ||
| 130 | |||
| 131 | 1 | it('does not create any hint when the limit of allowed hints was reached', (done) => { | |
| 132 | const filled = Array(50).fill(1); | ||
| 133 | 1 | const globals = {resourceHints: filled}; | |
| 134 | |||
| 135 | 1 | const path = '/my/styles.css'; | |
| 136 | 1 | const returned = addResourceHint( | |
| 137 | globals, | ||
| 138 | path, | ||
| 139 | 'preload', | ||
| 140 | 'style' | ||
| 141 | ); | ||
| 142 | |||
| 143 | 1 | expect(globals.resourceHints).to.have.length(filled.length); | |
| 144 | 1 | expect(returned).to.equals(path); | |
| 145 | 1 | done(); | |
| 146 | }); | ||
| 147 | |||
| 148 | 1 | it('should throw when invalid cors value is provided', done => { | |
| 149 | const f = () => addResourceHint({}, '/theme.css', 'style', 'preload', 'invalid-cors') ; |
||
| 150 | 1 | expect(f).to.throw(); | |
| 151 | 1 | done(); | |
| 152 | }); | ||
| 153 | |||
| 154 | 1 | it('should handle URLs with special chars', done => { | |
| 155 | const data = [ | ||
| 156 | { | ||
| 157 | original: "https://bigcommerce.paystackintegrations.com/js/script.js?store_hash=cag1k6vhir&installed_at=Tue May 10 2022 08:57:26 GMT+0000 (Coordinated Universal Time)", | ||
| 158 | expected: "https://bigcommerce.paystackintegrations.com/js/script.js?store_hash=cag1k6vhir&installed_at=Tue%20May%2010%202022%2008:57:26%20GMT+0000%20(Coordinated%20Universal%20Time)" | ||
| 159 | }, | ||
| 160 | { | ||
| 161 | original: `https://instocknotify.blob.core.windows.net/stencil/cfa17df4-72e5-4c23-a4c7-1fa6903bdeb4.js?ts=17443383" type="text/javascript""`, | ||
| 162 | expected: "https://instocknotify.blob.core.windows.net/stencil/cfa17df4-72e5-4c23-a4c7-1fa6903bdeb4.js?ts=17443383%22%20type=%22text/javascript%22%22" | ||
| 163 | }, | ||
| 164 | { | ||
| 165 | original: `https://instocknotify.blob.core.windows.net/stencil/41f9521c-57b6-4920-827b-77ba3477fcb5.js?ts=34911865" type="text/javascript"></script> <!--End InStockNotify Stencil Script -->`, | ||
| 166 | expected: "https://instocknotify.blob.core.windows.net/stencil/41f9521c-57b6-4920-827b-77ba3477fcb5.js?ts=34911865%22%20type=%22text/javascript%22%3E%3C/script%3E%20%3C!--End%20InStockNotify%20Stencil%20Script%20--%3E" | ||
| 167 | }, | ||
| 168 | { | ||
| 169 | original: `https://fonts.googleapis.com/css?family=Karla:400|Montserrat:400,500,700&display=block`, | ||
| 170 | expected: `https://fonts.googleapis.com/css?family=Karla:400%7CMontserrat:400,500,700&display=block` | ||
| 171 | }, | ||
| 172 | { | ||
| 173 | original: '/relative/asset/background.png?why=not', | ||
| 174 | expected: '/relative/asset/background.png?why=not' | ||
| 175 | } | ||
| 176 | ]; | ||
| 177 | 1 | const globals = {resourceHints: []}; | |
| 178 | |||
| 179 | 1 | data.forEach(pair => { | |
| 180 | const {original, expected} = pair; | ||
| 181 | |||
| 182 | 5 | const type = 'style'; | |
| 183 | 5 | const state = 'preload'; | |
| 184 | 5 | const cors = 'anonymous'; | |
| 185 | |||
| 186 | 5 | expect( | |
| 187 | expected, | ||
| 188 | addResourceHint(globals, original, state, type, cors) | ||
| 189 | ); | ||
| 190 | }); | ||
| 191 | 1 | done(); | |
| 192 | }); | ||
| 193 | |||
| 194 | 1 | it('should throw when invalid URL is passed in', done => { | |
| 195 | const globals = {resourceHints: []}; | ||
| 196 | 1 | const invalid = ''; | |
| 197 | const f = () => addResourceHint(globals, invalid, 'style', 'preload', 'no') |
||
| 198 | 1 | expect(f).to.throw(); | |
| 199 | 1 | done(); | |
| 200 | }); | ||
| 201 | }); | ||
| 202 | }); | ||
| 203 |
| Line | Lint | Hits | Source |
|---|---|---|---|
| 1 | 'use strict'; | ||
| 2 | |||
| 3 | 1 | const Code = require('code'); | |
| 4 | 1 | const Lab = require('lab'); | |
| 5 | 1 | const lab = exports.lab = Lab.script(); | |
| 6 | 1 | const expect = Code.expect; | |
| 7 | 1 | const it = lab.it; | |
| 8 | 1 | const describe = lab.describe; | |
| 9 | 1 | const helpers = require('../spec-helpers'); | |
| 10 | |||
| 11 | 1 | const AppError = require('../../lib/appError'); | |
| 12 | class CustomError extends AppError {}; | ||
| 13 | |||
| 14 | 1 | describe('AppError', () => { | |
| 15 | it('is an instance of Error', done => { | ||
| 16 | try { | ||
| 17 | 1 | throw new AppError(); | |
| 18 | } catch(e) { | ||
| 19 | 1 | expect(e instanceof Error).to.be.true(); | |
| 20 | } | ||
| 21 | 1 | done(); | |
| 22 | }); | ||
| 23 | |||
| 24 | 1 | it('keeps track of the message', done => { | |
| 25 | const msg = helpers.randomString(); | ||
| 26 | 1 | try { | |
| 27 | 1 | throw new AppError(msg); | |
| 28 | } catch(e) { | ||
| 29 | 1 | expect(e.message).to.equal(msg); | |
| 30 | } | ||
| 31 | 1 | done(); | |
| 32 | }); | ||
| 33 | |||
| 34 | 1 | it('keeps track of the extra details', done => { | |
| 35 | const msg = helpers.randomString(); | ||
| 36 | 1 | const data = helpers.randomString(); | |
| 37 | 1 | try { | |
| 38 | 1 | throw new AppError(msg, { data }); | |
| 39 | } catch(e) { | ||
| 40 | 1 | expect(e.details.data).to.equal(data); | |
| 41 | } | ||
| 42 | 1 | done(); | |
| 43 | }); | ||
| 44 | |||
| 45 | 1 | it('supplies default details hash', done => { | |
| 46 | const msg = helpers.randomString(); | ||
| 47 | 1 | try { | |
| 48 | 1 | throw new AppError(msg); | |
| 49 | } catch(e) { | ||
| 50 | 1 | expect(e.details).to.equal({}); | |
| 51 | } | ||
| 52 | 1 | done(); | |
| 53 | }); | ||
| 54 | |||
| 55 | 1 | it('makes the error name available', done => { | |
| 56 | try { | ||
| 57 | 1 | throw new AppError(); | |
| 58 | } catch(e) { | ||
| 59 | 1 | expect(e.name).to.equal('AppError'); | |
| 60 | } | ||
| 61 | 1 | done(); | |
| 62 | }); | ||
| 63 | |||
| 64 | 1 | it('does not include AppError constructor in stack trace', done => { | |
| 65 | const bug = () => { | ||
| 66 | throw new AppError(); | ||
| 67 | }; | ||
| 68 | |||
| 69 | 1 | try { | |
| 70 | 1 | bug(); | |
| 71 | } catch(e) { | ||
| 72 | 1 | expect(e.stack.includes('at bug')).to.be.true(); | |
| 73 | 1 | expect(e.stack.includes('at AppError')).to.be.false(); | |
| 74 | } | ||
| 75 | 1 | done(); | |
| 76 | }); | ||
| 77 | }); | ||
| 78 | |||
| 79 | 1 | describe('subclassing', () => { | |
| 80 | it('is an instance of Error', done => { | ||
| 81 | try { | ||
| 82 | 1 | throw new CustomError(); | |
| 83 | } catch(e) { | ||
| 84 | 1 | expect(e instanceof Error).to.be.true(); | |
| 85 | } | ||
| 86 | 1 | done(); | |
| 87 | }); | ||
| 88 | |||
| 89 | 1 | it('makes the error name available', done => { | |
| 90 | try { | ||
| 91 | 1 | throw new CustomError(); | |
| 92 | } catch(e) { | ||
| 93 | 1 | expect(e.name).to.equal('CustomError'); | |
| 94 | } | ||
| 95 | 1 | done(); | |
| 96 | }); | ||
| 97 | }); | ||
| 98 |